dsl-dynamic-stop-loss
npx skills add https://github.com/senpi-ai/senpi-skills --skill dsl-dynamic-stop-loss
Agent 安装分布
Skill 文档
Dynamic Stop Loss (DSL) v4
Automated trailing stop loss for leveraged perp positions on Hyperliquid. Monitors price via cron, ratchets profit floors upward through configurable tiers, and auto-closes positions on breach â no agent intervention required for the critical path.
Self-Contained Design
Script 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)
The script closes positions directly via mcporter. If the agent is slow, busy, or restarting, the position still gets closed on the next cron tick.
How It Works
Phase 1: “Let It Breathe” (uPnL < first tier)
- Wide retrace: 3% from high water mark
- Patient: requires 3 consecutive breach checks below floor
- Absolute floor: hard price floor to cap max loss
- Goal: Don’t get shaken out before the trade develops
Phase 2: “Lock the Bag” (uPnL ⥠first tier)
- Tight retrace: 1.5% from high water mark (or per-tier retrace)
- Quick exit: 1â2 consecutive breaches to close
- Tier floors: ratchet up as profit grows â never go back down
- Effective floor: best of tier floor and trailing floor
ROE-Based Tier Ratcheting
All tier triggers use ROE (Return on Equity): PnL / margin à 100. This means a triggerPct: 10 fires at 10% return on margin, not 10% price move. Leverage is accounted for automatically.
Tiers are defined as {triggerPct, lockPct} pairs. Each tier can optionally specify its own retrace value to tighten stops as profit grows:
"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}
]
The gap between trigger and lock (e.g., 10% trigger â 5% lock) gives breathing room so a minor pullback after hitting a tier doesn’t immediately close. Ratchets never go down â once you hit Tier 2, Tier 1’s floor is permanently superseded.
See references/tier-examples.md for LONG and SHORT worked examples with exact price calculations.
Direction Matters
â ï¸ CRITICAL â Getting direction backwards causes immediate false breaches or no protection at all. The script handles this automatically via the
directionfield, but double-check when initializing state files manually.
| LONG | SHORT | |
|---|---|---|
| Tier floor | entry à (1 + lockPct / 100 / leverage) |
entry à (1 - lockPct / 100 / leverage) |
| 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 | hw à (1 - retrace) |
hw à (1 + retrace) |
| Breach | price ⤠floor |
price ⥠floor |
| uPnL | (price - entry) Ã size |
(entry - price) Ã size |
Breach Decay
When price recovers above the floor:
"hard"(default): breach count resets to 0"soft": breach count decays by 1 per check
Soft mode is useful for volatile assets where price rapidly oscillates around the floor.
Floor Resolution
At each check, the effective floor is the best of:
- Tier floor â locked profit level (Phase 2 only)
- Trailing floor â from high water mark and retrace %
- Absolute floor â hard minimum (Phase 1 only)
For LONGs, “best” = maximum. For SHORTs, “best” = minimum.
Architecture
ââââââââââââââââââââââââââââââââââââââââââââ
â 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 â
ââââââââââââââââââââââââââââââââââââââââââââ
Files
| File | Purpose |
|---|---|
scripts/dsl-v4.py |
Core DSL engine â monitors, closes, outputs JSON |
| State file (JSON) | Per-position config + runtime state |
Multiple positions: Set DSL_STATE_FILE=/path/to/state.json to run separate instances per position. Each gets its own state file and cron job.
State File Schema
See references/state-schema.md for the complete schema with all fields documented.
Minimal required fields to create a new state file:
{
"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"
}
wallet is required â the script uses it to call close_position on breach.
Absolute Floor Calculation
- LONG:
entry à (1 - maxLoss% / leverage)â e.g., 10x with 3% â28.87 à (1 - 0.03/10)= $28.78 - SHORT:
entry à (1 + maxLoss% / leverage)â e.g., 7x with 3% â1955 à (1 + 0.03/7)= $1,963.38
Output JSON
The script prints a single JSON line per run. See references/output-schema.md for the complete schema.
Key fields for agent decision-making:
| Field | Agent action |
|---|---|
closed: true |
Alert user, disable cron |
pending_close: true |
Alert â close failed, retrying next tick |
tier_changed: true |
Notify user with tier details |
status: "error" |
Log; alert if consecutive_failures >= 3 |
breached: true |
Alert “â ï¸ BREACH X/X” |
distance_to_next_tier_pct < 2 |
Optionally notify approaching next tier |
Cron Setup
Per-position cron (every 3-5 min):
DSL_STATE_FILE=/data/workspace/dsl-state-BTC.json python3 scripts/dsl-v4.py
Stagger multiple positions by offsetting start times (:00, :01, :02).
How to Set Up a New Position
- Open position via Senpi API (
create_position) - Create a state file with position details (see schema above)
- Double-check
directionâ controls all LONG/SHORT math - Calculate
absoluteFloorcorrectly for the direction
- Double-check
- Create a cron job (every 3-5 min)
- DSL handles everything from there
When a Position Closes
- â
Script closes position via
mcporter call senpi close_position(with retry) - â
Script sets
active: false(orpendingClose: trueif close fails) - ð¤ Agent disables the cron (reads
closed=true) - ð¤ Agent sends alert to user
If close fails, script sets pendingClose: true and retries next cron tick.
Customization
See references/customization.md for conservative/moderate/aggressive presets and per-tier retrace tuning guidelines.
API Dependencies
- Price: Hyperliquid
allMidsAPI (direct HTTP, no auth) - Close position: Senpi
close_positionvia mcporter
â ï¸ Do NOT use
strategy_close_strategyto close individual positions. That closes the entire strategy (irreversible). Useclose_position.
Setup Checklist
- Extract
scripts/dsl-v4.pyandchmod +x - Ensure
mcporteris configured with Senpi auth - Create state file(s) per position
- Set up cron:
DSL_STATE_FILE=/path/to/state.json python3 scripts/dsl-v4.py - Agent reads output for alerts and cron cleanup
- If
pending_close=true, script auto-retries on next tick