skills/senpi-ai/senpi-skills/senpi-trading-runtime

senpi-trading-runtime

Installation
SKILL.md

Senpi Trading Runtime — OpenClaw Plugin

On-chain position tracker with automated DSL (Dynamic Stop-Loss) exit engine. Monitors a wallet's positions on Hyperliquid for lifecycle events (open, close, edit, flip) and applies two-phase trailing stop-loss protection to all positions. It links to an existing strategy wallet address and does not create wallets.

Core Concepts

Flow: Position Tracker scanner → detects position changes → DSL exit engine manages trailing stops

  1. Position Tracker (position_tracker scanner) polls the wallet on-chain, detecting opens, closes, increases, decreases, and flips.
  2. DSL exit engine monitors tracked positions on a timer, computing trailing stop-loss floors across two phases.
  3. Strategy (in runtime) = link to an existing wallet address. The wallet address is the strategy identifier everywhere, but wallet creation happens via Senpi MCP (not the runtime).

Key insight: The position tracker enables DSL protection for ALL positions of a strategy address — including those opened manually on the exchange or by other tools.

Wallet lifecycle (required order)

  1. Create strategy wallet address via Senpi MCP calls (or confirm it already exists).
  2. Put that wallet address into runtime YAML (strategies.<name>.wallet / ${WALLET_ADDRESS}).
  3. Install/create runtime. This links the runtime to the existing wallet for monitoring and exits.

Never treat openclaw senpi runtime create as wallet creation.

Strategy wallet validation (mandatory before runtime create/install)

Use this exact MCP-first guardrail to avoid using embedded wallets by mistake:

  1. Call strategy_list and collect strategies[].strategyWalletAddress.
  2. A runtime wallet is valid only if it appears in that list (case-insensitive match).
  3. If list is empty OR provided wallet is not in list: call strategy_create_custom_strategy to create a strategy wallet first.
  4. Use the newly created strategy wallet address directly as WALLET_ADDRESS in runtime YAML, then install runtime.

Hard rules:

  • Never use embedded wallet/injected wallet for runtime linking.
  • Never treat a wallet as a strategy wallet unless it exists in strategy_list.
  • Runtime install is blocked until strategy wallet validation passes.
  • Always confirm with the user before creating a new strategy wallet address, and explicitly confirm the budget (initialBudget) that will be used.

Example MCP flow (tool names from Senpi MCP):

strategy_list({})
if provided_wallet not in strategies[].strategyWalletAddress:
  # confirm with user first: create new strategy wallet + initialBudget
  strategy_create_custom_strategy({
    initialBudget: <budget_usdc>,
    positions: [],
    skillName: <strategy_or_runtime_name>,
    skillVersion: "1.0.0"
  })

Notes:

  • initialBudget is required when creating the strategy wallet.
  • Use positions: [] when you only need the strategy wallet created now and will trade later.

CLI Commands

All commands require the OpenClaw gateway running (openclaw gateway run).

Use openclaw senpi --cheatsheet to print the full plugin command cheatsheet to stdout.

Runtime management

Prerequisite: strategy wallet address already exists (created/fetched via Senpi MCP).

# Create a runtime from YAML file
openclaw senpi runtime create --path ./my-strategy.yaml
openclaw senpi runtime create -p ./my-strategy.yaml          # short form

# Create with inline YAML content
openclaw senpi runtime create --content "<yaml>"
openclaw senpi runtime create -c "<yaml>"                    # short form

# Create with custom ID
openclaw senpi runtime create --path ./my-strategy.yaml --runtime-id my-name

# List installed runtimes (id, wallet, source, status)
openclaw senpi runtime list

# Delete a runtime
openclaw senpi runtime delete <runtime_id>                   # positional id
openclaw senpi runtime delete --id <runtime_id>              # named flag

DSL position inspection

# All active DSL-tracked positions
openclaw senpi dsl positions
openclaw senpi dsl positions -r <id>                         # filter by runtime id
openclaw senpi dsl positions -a <addr>                       # filter by wallet address
openclaw senpi dsl positions --json

# Inspect one position (full DslState)
openclaw senpi dsl inspect <ASSET>
openclaw senpi dsl inspect SOL -r <id>
openclaw senpi dsl inspect SOL -a <addr>
openclaw senpi dsl inspect BTC --json

# Archived (closed) positions — reason, ROE, phase, tier
openclaw senpi dsl closes
openclaw senpi dsl closes -r <id>
openclaw senpi dsl closes -a <addr>
openclaw senpi dsl closes -l <n>
openclaw senpi dsl closes --json

Runtime diagnostics

openclaw senpi status                    # lightweight health summary (all runtimes)
openclaw senpi status -r <id>
openclaw senpi status --json

openclaw senpi state                     # full operational state (all runtimes)
openclaw senpi state -r <id>
openclaw senpi state --json

In-shell reference (senpi guide)

openclaw senpi guide                 # overview + quick command list
openclaw senpi guide scanners        # scanner types and config fields
openclaw senpi guide actions         # action types and decision modes
openclaw senpi guide dsl             # DSL exit engine: phases, tiers, time cuts
openclaw senpi guide examples        # print minimal strategy YAML to stdout
openclaw senpi guide schema          # full YAML schema field reference
openclaw senpi guide version         # plugin version and changelog URL

CLI preferences (senpi config)

Persists to ~/.openclaw/senpi-cli.json. Restart gateway to apply.

openclaw senpi config set-chat-id <chatId>
openclaw senpi config set-senpi-jwt-token <token>
openclaw senpi config set-state-dir <dir>
openclaw senpi config get <key>      # telegram-chat-id | senpi-jwt-token | state-dir
openclaw senpi config list           # secrets masked
openclaw senpi config unset <key>
openclaw senpi config reset

Gateway RPC (advanced)

Common calls:

openclaw gateway call senpi.installRuntime --params '{"runtimeYamlContent":"..."}'
openclaw gateway call senpi.listRuntimes --json --params '{}'
openclaw gateway call senpi.deleteRuntime --params '{"id":"my-runtime"}'
openclaw gateway call senpi.listDslPositions --json --params '{}'
openclaw gateway call senpi.getDslPositionState --json --params '{"asset":"SOL"}'
openclaw gateway call senpi.listDslArchives --json --params '{"limit":20}'
openclaw gateway call senpi.getHealthStatus --json --params '{}'
openclaw gateway call senpi.getSystemState --json --params '{}'

Methods summary

Method Params Success response
senpi.installRuntime runtimeYamlContent or runtimeYamlPath Runtime installed
senpi.listRuntimes List of runtimes
senpi.deleteRuntime id or address Deleted
senpi.listDslPositions runtimeId? Active positions
senpi.getDslPositionState asset, runtimeId? Single position state
senpi.listDslArchives runtimeId?, address?, limit? closes[] (asset, direction, entryPrice, lastPrice, currentROE, closeReason, closedAt, phase, …)
senpi.getHealthStatus runtimeId? (string) status or statuses[] (health, components.scanners, components.dsl)
senpi.getSystemState runtimeId? (string) state or states[] (health, stateDir, scanner/DSL components)

Close reasons for listDslArchives: dsl_breach, hard_timeout, weak_peak_cut, dead_weight_cut, exchange_sl_hit, manual.

Runtime YAML

The runtime YAML drives all behavior. Top-level keys: name, strategies, scanners, actions, exit, notifications.

name: my-tracker
version: 1.0.0
description: >
  On-chain position tracker with DSL trailing stop-loss.

strategies:
  main:
    wallet: "${WALLET_ADDRESS}"
    budget: 500
    slots: 2
    margin_per_slot: 200
    trading_risk: conservative    # conservative | moderate | aggressive
    enabled: true

scanners:
  - name: position_tracker
    type: position_tracker
    interval: 10s

actions:
  - name: position_tracker_action
    action_type: POSITION_TRACKER
    decision_mode: rule
    scanners: [position_tracker]

exit:
  engine: dsl
  interval_seconds: 30            # how often the price monitor runs (5-3600)
  dsl_preset:                     # single preset (no named map needed)
    hard_timeout:
      enabled: true
      interval_in_minutes: 360
    weak_peak_cut:
      enabled: true
      interval_in_minutes: 120
      min_value: 5
    dead_weight_cut:
      enabled: true
      interval_in_minutes: 60
    phase1:
      enabled: true
      max_loss_pct: 4.0
      retrace_threshold: 7
      consecutive_breaches_required: 1
    phase2:
      enabled: true
      tiers:
        - { trigger_pct: 7,  lock_hw_pct: 40 }
        - { trigger_pct: 12, lock_hw_pct: 55 }
        - { trigger_pct: 15, lock_hw_pct: 75 }
        - { trigger_pct: 20, lock_hw_pct: 85 }

notifications:
  telegram_chat_id: "${TELEGRAM_CHAT_ID}"

Environment variable substitution: ${VAR} and ${VAR:-default} resolved at load time.

For full field details: YAML Schema Reference

DSL Exit Engine — Key Concepts

Two-phase trailing stop-loss protecting open positions:

Phase 1 (from entry until the first tier is reached):

  • Absolute floor from max_loss_pct (cap on loss from entry, scaled by leverage)
  • Trailing floor from retrace_threshold (ROE % pullback from high-water mark)
  • Effective floor = stricter of both (LONG: higher price; SHORT: lower)
  • Optional time-based cuts at preset level: hard_timeout, weak_peak_cut, dead_weight_cut
  • Exchange SL stays at the absolute floor only; tighter exits enforced by runtime

Phase 2 (after the first tier is reached — Phase 2 always starts at tier 0):

  • Tier floor = lock_hw_pct % of current high-water ROE (trails as HW advances, never loosens)
  • Exchange SL tracks the full effective floor and updates as it moves

Tiers are profit milestones (by ROE % from entry). Each tier has two fields: trigger_pct (ROE % that activates it) and lock_hw_pct (% of high-water ROE to lock as floor). Tiers must have strictly increasing trigger_pct.

Consecutive breaches are tick-based (not time-based). Each monitor tick (every interval_seconds), if price violates the floor, the breach counter increments. Recovery resets it.

Retrace convention: retrace_threshold is ROE %. Engine converts to price: retrace_ROE% / 100 / leverage. At 10x leverage, 7% ROE retrace = 0.7% price below high-water.

For full DSL configuration with all fields, time-based cuts, close reasons, and events: DSL Configuration Reference

Environment Variables

Variable Required Purpose
WALLET_ADDRESS Yes Existing strategy wallet address from Senpi MCP (used in YAML via ${WALLET_ADDRESS}).
SENPI_API_KEY For live MCP Senpi MCP authentication.
TELEGRAM_CHAT_ID No Telegram chat ID for notifications.
DSL_STATE_DIR No Override DSL state file directory.

References

Weekly Installs
32
GitHub Stars
75
First Seen
3 days ago