@moonpay/cli 1.43.0 → 1.44.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -40,7 +40,7 @@ AMOUNT=5
40
40
 
41
41
  # --- Execute ---
42
42
  log "SWAP: $AMOUNT $FROM_TOKEN -> $TO_TOKEN on $CHAIN"
43
- RESULT=$("$MP" -f compact token swap \
43
+ RESULT=$("$MP" --json token swap \
44
44
  --wallet "$WALLET" --chain "$CHAIN" \
45
45
  --from-token "$FROM_TOKEN" --from-amount "$AMOUNT" \
46
46
  --to-token "$TO_TOKEN" 2>&1) || {
@@ -51,11 +51,23 @@ log "OK: $RESULT"
51
51
  ```
52
52
 
53
53
  Key points:
54
- - `mp -f compact` outputs single-line JSON, ideal for `jq` parsing
54
+ - `mp --json` outputs single-line JSON, ideal for `jq` parsing
55
55
  - Use `$(which mp)` and store as `MP` — cron/launchd have minimal PATH
56
56
  - Wallet names only in scripts — `mp` handles keychain decryption at runtime
57
57
  - If the user gives token names/symbols, resolve to addresses first with `mp token search`
58
58
 
59
+ ## Pre-built strategy skills
60
+
61
+ For three common strategies, prefer the dedicated skills over rolling your own:
62
+
63
+ - **moonpay-trailing-stop** — Sell automatically if price drops X% from its peak.
64
+ - **moonpay-take-profit-ladder** — Sell in chunks at preset price targets, hold the runner.
65
+ - **moonpay-buy-the-dip** — Buy a fixed USD amount when 24h drawdown exceeds a threshold, with a cooldown.
66
+
67
+ Each one comes with a verified shell-script template, the right cron cadence, and notes on common pitfalls. The patterns below (DCA, generic limit order, generic stop loss) are still the right starting point for strategies that don't fit those three.
68
+
69
+ If the user is on the **MoonPay Agents desktop app**, recommend using its in-app automation panel instead of generating a shell script — the app provides a persistent chat session that holds strategy state across fires (high-water-mark, fired rungs, last-fire timestamp) without a separate `state.json` file. The shell-script + cron pattern below is the right choice for headless / server / CI contexts where the app isn't running.
70
+
59
71
  ## DCA (Dollar-Cost Averaging)
60
72
 
61
73
  "Buy $5 of SOL every day at 9am"
@@ -137,7 +149,7 @@ TARGET_PRICE=80
137
149
  SCRIPT_NAME="limit-buy-sol"
138
150
 
139
151
  # --- Check price ---
140
- PRICE=$("$MP" -f compact token retrieve --token "$TOKEN" --chain "$CHAIN" | jq -r '.marketData.price')
152
+ PRICE=$("$MP" --json token retrieve --token "$TOKEN" --chain "$CHAIN" | jq -r '.marketData.price')
141
153
 
142
154
  if [ -z "$PRICE" ] || [ "$PRICE" = "null" ]; then
143
155
  log "LIMIT $SCRIPT_NAME: price fetch failed, skipping"
@@ -147,7 +159,7 @@ fi
147
159
  # --- Compare ---
148
160
  if (( $(echo "$PRICE < $TARGET_PRICE" | bc -l) )); then
149
161
  log "LIMIT $SCRIPT_NAME: price $PRICE < $TARGET_PRICE — executing buy"
150
- RESULT=$("$MP" -f compact token swap \
162
+ RESULT=$("$MP" --json token swap \
151
163
  --wallet "$WALLET" --chain "$CHAIN" \
152
164
  --from-token "$BUY_WITH" --from-amount "$BUY_AMOUNT" \
153
165
  --to-token "$TOKEN" 2>&1) || {
@@ -186,16 +198,16 @@ TRIGGER_PRICE=70
186
198
  SCRIPT_NAME="stop-loss-sol"
187
199
 
188
200
  # --- Check price ---
189
- PRICE=$("$MP" -f compact token retrieve --token "$SELL_TOKEN" --chain "$CHAIN" | jq -r '.marketData.price')
201
+ PRICE=$("$MP" --json token retrieve --token "$SELL_TOKEN" --chain "$CHAIN" | jq -r '.marketData.price')
190
202
 
191
203
  if (( $(echo "$PRICE < $TRIGGER_PRICE" | bc -l) )); then
192
204
  # Get current balance to sell all
193
- BALANCE=$("$MP" -f compact token balance list --wallet "$WALLET" --chain "$CHAIN" \
205
+ BALANCE=$("$MP" --json token balance list --wallet "$WALLET" --chain "$CHAIN" \
194
206
  | jq -r --arg addr "$SELL_TOKEN" '.items[] | select(.address == $addr) | .balance.amount')
195
207
 
196
208
  if [ -n "$BALANCE" ] && (( $(echo "$BALANCE > 0" | bc -l) )); then
197
209
  log "STOP-LOSS $SCRIPT_NAME: price $PRICE < $TRIGGER_PRICE — selling $BALANCE"
198
- RESULT=$("$MP" -f compact token swap \
210
+ RESULT=$("$MP" --json token swap \
199
211
  --wallet "$WALLET" --chain "$CHAIN" \
200
212
  --from-token "$SELL_TOKEN" --from-amount "$BALANCE" \
201
213
  --to-token "$TO_TOKEN" 2>&1) || {
@@ -270,6 +282,9 @@ fi
270
282
 
271
283
  ## Related skills
272
284
 
285
+ - **moonpay-trailing-stop** — Mechanical trailing stop with shipped reference script
286
+ - **moonpay-take-profit-ladder** — Laddered partial sells at preset price targets
287
+ - **moonpay-buy-the-dip** — Conditional buy on drawdown with cooldown
273
288
  - **moonpay-swap-tokens** — Swap and bridge command syntax
274
289
  - **moonpay-check-wallet** — Check balances before setting up automation
275
290
  - **moonpay-discover-tokens** — Research tokens and resolve addresses
@@ -0,0 +1,153 @@
1
+ ---
2
+ name: moonpay-trailing-stop
3
+ description: Run a trailing stop on one position — sell automatically if the price drops a fixed % from its peak since you started watching. Mechanical execution, no overrides.
4
+ tags: [trading, automation, stop-loss]
5
+ ---
6
+
7
+ # Trailing stop
8
+
9
+ ## Goal
10
+
11
+ Watch a single position and automatically sell it into a stable (USDC by default) if its price falls a fixed percent from the high-water-mark recorded since the strategy started. The whole point is mechanical execution — never widen the stop because "it'll come back."
12
+
13
+ This skill works in two contexts:
14
+ - **Inside an automation harness** (the MoonPay Agents desktop app): chat session memory holds the high-water-mark across fires.
15
+ - **Headless via cron / launchd**: a JSON file at `~/.config/moonpay/state/trailing-stop-<id>.json` holds the same state.
16
+
17
+ The decision logic is identical; only the persistence layer differs.
18
+
19
+ ## Prerequisites
20
+
21
+ - `mp` authenticated: `mp user retrieve`
22
+ - The target position is held in a wallet `mp wallet list` knows about
23
+ - `jq` installed: `which jq`
24
+
25
+ ## Configuration
26
+
27
+ Required inputs to capture on first run (ask the user, then persist):
28
+
29
+ | Field | Notes |
30
+ |---|---|
31
+ | `token` | Token address. Resolve from a ticker with `mp token search --query <ticker> --chain <chain>` |
32
+ | `chain` | e.g. `solana`, `ethereum`, `base` |
33
+ | `wallet` | Wallet name (the local label, not the address) |
34
+ | `trailing_pct` | Percent drop from peak that triggers the sell. Default: 12 |
35
+ | `sell_into` | Token address to sell into. Default: USDC on the same chain |
36
+ | `entry_price` | Optional — original cost basis if known. Used only for PnL reporting at exit |
37
+ | `hwm` | Initial high-water-mark = current price at first fire |
38
+ | `closed` | Boolean. False until the stop fires. After it fires, the strategy stops watching |
39
+
40
+ ## Per-fire workflow
41
+
42
+ ```
43
+ 1. If `closed`: log "position already closed" and exit.
44
+ 2. price = mp --json token retrieve --token $TOKEN --chain $CHAIN | jq '.marketData.price'
45
+ 3. balance = mp --json token balance list --wallet $WALLET_ADDR --chain $CHAIN
46
+ | jq --arg t $TOKEN '.items[] | select(.address==$t) | .balance.amount'
47
+ 4. If balance is null or 0: mark closed=true, log "no position, stopping", exit.
48
+ 5. If price > hwm: hwm = price. Log "new HWM $price (drawdown 0.00%)". Exit.
49
+ 6. drawdown_pct = (hwm - price) / hwm * 100
50
+ 7. If drawdown_pct >= trailing_pct: EXECUTE THE SELL (full balance). Log fill.
51
+ Mark closed=true. Optional: report PnL vs entry_price if known.
52
+ 8. Else: log "holding — price $price, HWM $hwm, drawdown $drawdown_pct%, gap to trigger $(trailing_pct - drawdown_pct)%". Exit.
53
+ ```
54
+
55
+ The sell command:
56
+
57
+ ```bash
58
+ mp --json token swap \
59
+ --wallet "$WALLET" \
60
+ --chain "$CHAIN" \
61
+ --from-token "$TOKEN" \
62
+ --from-amount "$BALANCE" \
63
+ --to-token "$SELL_INTO"
64
+ ```
65
+
66
+ ## Schedule
67
+
68
+ A 15-minute interval is the right balance for most positions:
69
+ - Cron: `*/15 * * * * /path/to/script.sh # moonpay:trailing-stop-<id>`
70
+ - Launchd: `<key>StartInterval</key><integer>900</integer>`
71
+
72
+ For volatile small-caps, drop to `*/5`. For large caps you're not actively trading, hourly is enough.
73
+
74
+ ## Headless reference script
75
+
76
+ ```bash
77
+ #!/bin/bash
78
+ set -euo pipefail
79
+ MP="$(which mp)"
80
+ ID="trailing-stop-sol-main" # unique per strategy
81
+ STATE="$HOME/.config/moonpay/state/${ID}.json"
82
+ LOG="$HOME/.config/moonpay/logs/trading.log"
83
+ mkdir -p "$(dirname "$STATE")" "$(dirname "$LOG")"
84
+ log() { echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] $ID $*" >> "$LOG"; }
85
+
86
+ # --- Config (fill these in once) ---
87
+ TOKEN="So11111111111111111111111111111111111111112" # SOL (WSOL mint)
88
+ CHAIN="solana"
89
+ WALLET="main"
90
+ WALLET_ADDR="<address from mp wallet list>"
91
+ TRAILING_PCT=12
92
+ SELL_INTO="EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" # USDC
93
+
94
+ # --- Load state (initialize on first run) ---
95
+ if [ ! -f "$STATE" ]; then
96
+ PRICE=$("$MP" --json token retrieve --token "$TOKEN" --chain "$CHAIN" | jq '.marketData.price')
97
+ echo "{\"hwm\": $PRICE, \"closed\": false}" > "$STATE"
98
+ log "initialized hwm=$PRICE"
99
+ fi
100
+ CLOSED=$(jq -r '.closed' "$STATE")
101
+ HWM=$(jq -r '.hwm' "$STATE")
102
+ [ "$CLOSED" = "true" ] && { log "already closed, exit"; exit 0; }
103
+
104
+ # --- Check price + balance ---
105
+ PRICE=$("$MP" --json token retrieve --token "$TOKEN" --chain "$CHAIN" | jq '.marketData.price')
106
+ BALANCE=$("$MP" --json token balance list --wallet "$WALLET_ADDR" --chain "$CHAIN" \
107
+ | jq --arg t "$TOKEN" '[.items[] | select(.address==$t) | .balance.amount][0] // 0')
108
+
109
+ if (( $(echo "$BALANCE <= 0" | bc -l) )); then
110
+ jq '.closed = true' "$STATE" > "$STATE.tmp" && mv "$STATE.tmp" "$STATE"
111
+ log "no position, marked closed"; exit 0
112
+ fi
113
+
114
+ # --- Decide ---
115
+ if (( $(echo "$PRICE > $HWM" | bc -l) )); then
116
+ jq --argjson p "$PRICE" '.hwm = $p' "$STATE" > "$STATE.tmp" && mv "$STATE.tmp" "$STATE"
117
+ log "new HWM $PRICE"
118
+ exit 0
119
+ fi
120
+
121
+ DRAWDOWN_PCT=$(echo "scale=4; ($HWM - $PRICE) / $HWM * 100" | bc -l)
122
+ TRIGGER=$(echo "$DRAWDOWN_PCT >= $TRAILING_PCT" | bc -l)
123
+ if [ "$TRIGGER" = "1" ]; then
124
+ log "TRIGGER: drawdown ${DRAWDOWN_PCT}% >= ${TRAILING_PCT}% — selling $BALANCE"
125
+ RESULT=$("$MP" --json token swap \
126
+ --wallet "$WALLET" --chain "$CHAIN" \
127
+ --from-token "$TOKEN" --from-amount "$BALANCE" \
128
+ --to-token "$SELL_INTO" 2>&1) || { log "SELL FAILED: $RESULT"; exit 1; }
129
+ log "SELL OK: $RESULT"
130
+ jq '.closed = true' "$STATE" > "$STATE.tmp" && mv "$STATE.tmp" "$STATE"
131
+ else
132
+ log "holding — price $PRICE hwm $HWM drawdown ${DRAWDOWN_PCT}%"
133
+ fi
134
+ ```
135
+
136
+ ## Inside an automation harness
137
+
138
+ When this skill runs inside the MoonPay Agents desktop app, the harness provides a single resumed chat session — no state file needed. The agent persists `hwm` and `closed` in conversation memory and follows the same decision logic. The body of the in-app automation is a thin pointer to this skill.
139
+
140
+ ## Common pitfalls
141
+
142
+ - **Hard-coding the SOL mint as `...111`**. The wrapped SOL mint ends in `112`. Resolve via `mp token search --query SOL --chain solana` if unsure.
143
+ - **Skipping the balance check**. If the user manually sells the position between fires, the sell command will fail with insufficient balance. Always check balance first; mark closed and exit if zero.
144
+ - **Widening the stop after a loss**. The strategy is defined by mechanical execution. If you find yourself wanting to override, the strategy isn't for this position.
145
+ - **Not naming the cron entry**. Always tag scheduled entries with `# moonpay:trailing-stop-<id>` so they can be listed and removed cleanly.
146
+
147
+ ## Related skills
148
+
149
+ - **moonpay-trading-automation** — Shell-script + cron/launchd patterns for headless automation
150
+ - **moonpay-take-profit-ladder** — Companion strategy for partial-sell-on-the-way-up
151
+ - **moonpay-discover-tokens** — Resolve tickers to token addresses
152
+ - **moonpay-swap-tokens** — Underlying swap command syntax
153
+ - **moonpay-check-wallet** — Position size lookup