DSL / Ratchet Stop: Architecture and Phase Handoff

Updated May 22, 2026 · 2 min read

How the DSL/Ratchet Stop engine runs loss protection then profit trailing, and hands off to the backend so positions stay protected offline.

DSL (Dynamic Stop-Loss) is Senpi's automatic exit engine. Ratchet Stop is the user-facing name for the same DSL engine - the MCP tools ratchet_stop_* translate transparently to internal dsl_* naming. Same system, different namespace.

Exit pipeline

Scanner

decides entry

Runtime DSL

Phase 1: loss protection

Phase 2 local

trail until tier 1

Backend Ratchet Stop

server-side trailing

Hyperliquid SL

real stop order

Where DSL lives

DSL is built into @senpi-ai/runtime (article 20) and is configured via the exit: block in your strategy YAML (engine: dsl, dsl_preset: with phase1/phase2/tiers). The legacy dsl-dynamic-stop-loss skill (Python + cron + per-skill state files) is still maintained for skills and positions not yet migrated, but is not recommended for new work. New skills and agents should use the runtime DSL. Existing skills are being migrated skill-by-skill - see DSL-MIGRATION-PLAYBOOK.md in Senpi-ai/senpi-skills. Already migrated: ORCA, FOX, MANTIS, ROACH.

Phase 1 - Loss Protection

Monitors unrealized ROE against a dynamic floor with retrace detection and consecutive-breach counting. Optional time-based exits live here too: hard timeout, dead weight cut, weak peak cut (preset-root keys). Configuration details in article 24.

Phase 2 - Profit Trailing

Once ROE crosses the first tier, the effective floor tracks lock_hw_pct of high-water ROE and the exchange stop-loss is updated each tick. The floor only moves up, never down.

Local-to-backend handoff (offline protection)

The runtime can hand off Phase 2 to the backend Ratchet Stop service (via ratchet_stop_add) so positions stay protected even when your agent is offline. Once a position hits its first tier trigger, the backend takes over profit trailing server-side. It uses real-time websocket prices (sub-second) and places actual stop-loss orders on Hyperliquid. Positions are protected even if your agent crashes, your Railway instance runs out of memory, or your VM goes down. The handoff is seamless with zero protection gap.

*The six `ratchet_stop_` MCP tools** are the programmatic API for the backend service:

  • ratchet_stop_add - add a tiered ROE-based stop-loss config (triggerRoe / lockRoe pairs).
  • ratchet_stop_edit - edit tier breakpoints on an active position.
  • ratchet_stop_delete - delete a config.
  • ratchet_stop_get - query state.
  • ratchet_stop_list - list active configs.
  • ratchet_stop_events - stream config-change events.

Scanner vs DSL: who closes positions?

Option A

Scanner - Entries only

  • Decides what to trade and when
  • ✗ Never re-evaluate open positions
  • ✗ Never close on "thesis invalidation"

Option B

DSL - Exits only

  • Handles all exits
  • Loss protection + profit trailing
  • Lets the one big winner run

VerdictThe scanner decides when to enter. DSL decides when to exit. Do not have both trying to close positions.

For tier/timing configuration, see article 24. For troubleshooting DSL state, see article 25.

Runtime source

Browse the ratchet-stop implementation.

View on GitHub
Share

Related articles