Loading...
Loading...
Automated trailing stop loss for leveraged perpetual positions on Hyperliquid. Monitors price via cron, ratchets profit floors through configurable tiers, and auto-closes positions on breach via mcporter — no agent intervention for the critical path. Works for LONG and SHORT. ROE-based (return on margin) tier triggers that automatically account for leverage. Use when protecting an open Hyperliquid perp position, setting up trailing stops, managing profit tiers, or automating position exits on breach.
npx skill4agent add senpi-ai/senpi-skills dsl-dynamic-stop-lossScript handles: Agent handles:
✅ Price monitoring 📢 Telegram alerts
✅ High water tracking 🧹 Cron cleanup (disable after close)
✅ Tier upgrades 📊 Portfolio reporting
✅ Breach detection 🔄 Retry awareness (pendingClose alerts)
✅ Position closing (via mcporter, with retry)
✅ State deactivation
✅ Error handling (fetch failures)mcporterPnL / margin × 100triggerPct: 10{triggerPct, lockPct}retrace"tiers": [
{"triggerPct": 10, "lockPct": 5},
{"triggerPct": 20, "lockPct": 14},
{"triggerPct": 30, "lockPct": 22, "retrace": 0.012},
{"triggerPct": 50, "lockPct": 40, "retrace": 0.010},
{"triggerPct": 75, "lockPct": 60, "retrace": 0.008},
{"triggerPct": 100, "lockPct": 80, "retrace": 0.006}
]⚠️ CRITICAL — Getting direction backwards causes immediate false breaches or no protection at all. The script handles this automatically via thefield, but double-check when initializing state files manually.direction
| LONG | SHORT | |
|---|---|---|
| Tier floor | | |
| Absolute floor | Below entry (e.g., entry × 0.97) | Above entry (e.g., entry × 1.03) |
| High water | Highest price seen | Lowest price seen |
| Trailing floor | | |
| Breach | | |
| uPnL | | |
"hard""soft"┌──────────────────────────────────────────┐
│ Cron: every 3-5 min (per position) │
├──────────────────────────────────────────┤
│ scripts/dsl-v4.py │
│ • Reads state from JSON file │
│ • Fetches price from allMids API │
│ • Direction-aware (LONG + SHORT) │
│ • Updates high water mark │
│ • Checks tier upgrades (ROE-based) │
│ • Per-tier retrace override │
│ • Calculates effective floor │
│ • Detects breaches (with decay modes) │
│ • ON BREACH: closes via mcporter w/retry │
│ • pendingClose if close fails │
│ • Outputs enriched JSON status │
├──────────────────────────────────────────┤
│ Agent reads JSON output: │
│ • closed=true → alert user, disable cron │
│ • pending_close=true → alert, will retry │
│ • tier_changed=true → notify user │
│ • status=error → log, check failures │
│ • Otherwise → silent │
└──────────────────────────────────────────┘| File | Purpose |
|---|---|
| Core DSL engine — monitors, closes, outputs JSON |
| State file (JSON) | Per-position config + runtime state |
DSL_STATE_FILE=/path/to/state.json{
"active": true,
"asset": "HYPE",
"direction": "LONG",
"leverage": 10,
"entryPrice": 28.87,
"size": 1890.28,
"wallet": "0xYourStrategyWalletAddress",
"strategyId": "uuid-of-strategy",
"phase": 1,
"phase1": {
"retraceThreshold": 0.03,
"consecutiveBreachesRequired": 3,
"absoluteFloor": 28.00
},
"phase2TriggerTier": 1,
"phase2": {
"retraceThreshold": 0.015,
"consecutiveBreachesRequired": 2
},
"tiers": [
{"triggerPct": 10, "lockPct": 5},
{"triggerPct": 20, "lockPct": 14},
{"triggerPct": 30, "lockPct": 22, "retrace": 0.012},
{"triggerPct": 50, "lockPct": 40, "retrace": 0.010},
{"triggerPct": 75, "lockPct": 60, "retrace": 0.008},
{"triggerPct": 100, "lockPct": 80, "retrace": 0.006}
],
"currentTierIndex": -1,
"tierFloorPrice": null,
"highWaterPrice": 28.87,
"floorPrice": 28.00,
"currentBreachCount": 0,
"createdAt": "2026-02-20T15:22:00.000Z"
}walletclose_positionentry × (1 - maxLoss% / leverage)28.87 × (1 - 0.03/10)entry × (1 + maxLoss% / leverage)1955 × (1 + 0.03/7)| Field | Agent action |
|---|---|
| Alert user, disable cron |
| Alert — close failed, retrying next tick |
| Notify user with tier details |
| Log; alert if |
| Alert "⚠️ BREACH X/X" |
| Optionally notify approaching next tier |
DSL_STATE_FILE=/data/workspace/dsl-state-BTC.json python3 scripts/dsl-v4.pycreate_positiondirectionabsoluteFloormcporter call senpi close_positionactive: falsependingClose: trueclosed=truependingClose: trueallMidsclose_position⚠️ Do NOT useto close individual positions. That closes the entire strategy (irreversible). Usestrategy_close_strategy.close_position
scripts/dsl-v4.pychmod +xmcporterDSL_STATE_FILE=/path/to/state.json python3 scripts/dsl-v4.pypending_close=true