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/lockRoepairs).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.
Related articles
DSL Troubleshooting and State Validation
Check active status, core state fields, and field naming to fix broken DSL, and confirm a zero-trade agent is actually running.
DSL Configuration: Tiers, Time-Based Exits, and Asset Tuning
Configure DSL trailing stop tiers, time-based exits, and per-asset timing, and edit exit parameters on active positions.
