Skip to content

Recipes

Pim Feltkamp edited this page Apr 27, 2026 · 4 revisions

Recipes — day-to-day workflows

Practical, copyable patterns for using the cryptohopper CLI interactively. For CI/automation flows (--json, exit codes, jq pipelines, cron) see Scripting — this page is what to type at a terminal when you actually want to look at something.

Every example assumes you've run cryptohopper login once. The CLI persists your token in ~/.cryptohopper/config.json (mode 0600).

Contents

Find your most active hopper

cryptohopper hoppers list --json | jq '
  .data
  | map({id, name, exchange, position_count: (.positions // 0)})
  | sort_by(-.position_count)
  | .[0:5]
'

Top-5 hoppers by current open-position count. Useful when you have many bots and want to focus on the busy ones.

See what every hopper is currently holding

for id in $(cryptohopper hoppers list --json | jq -r '.data[].id'); do
  name=$(cryptohopper hoppers get "$id" --json | jq -r '.data.name')
  echo "── $name (#$id) ──"
  cryptohopper positions "$id"
done

Prints a tabbed snapshot per hopper. positions (no --json) renders a Unicode table — easy to read in a terminal. For machine-readable output, add --json and pipe to jq.

Watch a ticker in your terminal

watch -n 3 'cryptohopper ticker binance BTC/USDT'

Refreshes every 3 seconds. cryptohopper ticker still requires you to be logged in (run cryptohopper login first). Although /exchange/ticker is conceptually "public market data," the API gateway requires an OAuth bearer on every call today.

For a multi-pair watcher:

watch -n 5 '
  for pair in BTC/USDT ETH/USDT SOL/USDT; do
    cryptohopper ticker binance "$pair" --json | jq -r "\"\($pair) \(.data.last)\""
  done
'

Kick off a 30-day backtest and wait for it

hopper_id=42
from=$(date -d "30 days ago" +%Y-%m-%d)
to=$(date +%Y-%m-%d)

bt_id=$(cryptohopper backtest new "$hopper_id" --from "$from" --to "$to" --json \
        | jq -r '.data.id')

echo "Submitted backtest #$bt_id, polling..."
while :; do
  status=$(cryptohopper backtest status "$bt_id" --json | jq -r '.data.status')
  echo "  status=$status"
  case "$status" in
    completed|failed) break ;;
  esac
  sleep 5
done

cryptohopper backtest status "$bt_id"

Backtests have a separate rate bucket (1 request per 2 seconds). 5-second polling stays well clear.

Compare two strategies side-by-side

cryptohopper strategy get 5  > /tmp/strategy-A.json
cryptohopper strategy get 12 > /tmp/strategy-B.json
diff -u /tmp/strategy-A.json /tmp/strategy-B.json

strategy get pretty-prints the full JSON config. Diffing two strategies tells you exactly what differs — useful when iterating on a copy.

Browse the marketplace

# Note: the API gateway requires a real OAuth bearer on every call.
cryptohopper signals list --type buy --limit 20
cryptohopper template list
cryptohopper tournaments active

The first one shows live buy signals from the marketplace, the second shows your saved templates, the third shows currently-running tournaments.

Quick health check before you trade

cryptohopper whoami                  # confirm which account is active
cryptohopper subscription get        # confirm plan and remaining bots
cryptohopper backtest limits         # confirm backtest quota
cryptohopper subscription credits    # confirm AI credits

A 4-command health check before you start a session. Each is one quick GET — no rate-limit pressure.

Inspect open positions and orders together

hopper_id=42
echo "── positions ──"
cryptohopper positions "$hopper_id"
echo
echo "── open orders ──"
cryptohopper orders "$hopper_id"

Two side-by-side tables. Useful when reviewing what a bot is doing right now, before deciding whether to intervene.

Stop a runaway bot fast

cryptohopper hoppers panic 42 --yes

panic cancels every open order and closes every position immediately. The --yes skips the confirmation gate; don't add --yes until you're sure. Without it the CLI prompts and waits — that's the safe default.

For machine-readable output (e.g. you want to check what got cancelled in a script), add --json --yes. The --yes is required even with --json — the CLI explicitly refuses to silently bypass confirmation for --json callers (see the 0.5.1-alpha.1 changelog for the underlying fix).

Switch between accounts (work / personal)

The CLI reads CRYPTOHOPPER_TOKEN from the env before falling back to ~/.cryptohopper/config.json. So per-shell account switching is trivial:

# Personal account: use the saved config.
cryptohopper whoami

# Work account: override via env, leave saved config alone.
CRYPTOHOPPER_TOKEN=$WORK_BEARER cryptohopper whoami

Or with shell functions in your .bashrc / .zshrc:

ch-work()     { CRYPTOHOPPER_TOKEN="$WORK_BEARER" cryptohopper "$@"; }
ch-personal() { cryptohopper "$@"; }  # uses saved config

CRYPTOHOPPER_APP_KEY follows the same precedence rule.

Self-update without leaving the terminal

cryptohopper upgrade --check        # is there a newer release?
cryptohopper upgrade                # download + verify SHA256 + atomic swap

upgrade only works for the standalone Bun-compiled binaries. If you installed via npm install -g @cryptohopper/cli, the CLI prints a hint to use npm instead.

Diagnose an auth failure end-to-end

When something goes wrong with your token, the right next step depends on which error you're seeing. This sequence narrows it down in 30 seconds.

# 1. CLI version sanity check. Earlier versions sent the wrong auth header
#    (Authorization: Bearer instead of access-token); 0.6.0-alpha.2+ is fixed.
cryptohopper --version

# 2. Token presence + length sanity check.
[ -n "$CRYPTOHOPPER_TOKEN" ] \
  && echo "env var: ${#CRYPTOHOPPER_TOKEN} chars (expected 40)" \
  || echo "env var: not set (will fall back to ~/.cryptohopper/config.json)"

# 3. Config file presence + perms.
ls -l ~/.cryptohopper/config.json 2>&1

# 4. The minimal authenticated probe.
cryptohopper whoami --json

The output of step 4 tells you everything:

Output What's wrong Fix
{"ok": true, ...email/username...} Nothing — auth works (none)
{"ok": false, "error": {"code": "UNAUTHORIZED"}} Token is invalid, expired, or revoked Re-run cryptohopper login, or re-issue the token in the developer dashboard
{"ok": false, "error": {"code": "FORBIDDEN", "ip_address": "1.2.3.4"}} Token is valid but your IP isn't on the OAuth app's allowlist Add 1.2.3.4 to the OAuth app's IP allowlist on cryptohopper.com, or disable the allowlist for that app
{"ok": false, "error": {"code": "FORBIDDEN", ...}} (no ip_address) Token is valid but lacks the required scope Re-issue the token with the right scopes (read manage trade user)
{"ok": false, "error": {"message": "Missing Authentication Token"}} The CLI is sending the wrong auth header (likely 0.6.0-alpha.1) cryptohopper upgrade to 0.6.0-alpha.2+
{"ok": false, "error": {"code": "NETWORK_ERROR"}} Can't reach api.cryptohopper.com Check DNS, firewall, corporate proxy (HTTPS_PROXY env var)
{"ok": false, "error": {"code": "TIMEOUT"}} Reached the API but it didn't respond within 30s Retry; if persistent, check Cryptohopper status page

Common quick-fixes inferred from the matrix:

# Re-auth from scratch (drops the saved token, re-runs the OAuth browser flow)
cryptohopper logout && cryptohopper login

# Verify which token is actually being used (env var beats config file)
env | grep CRYPTOHOPPER_TOKEN || echo "no env var; config file in use"

# Print the config file (with token masked)
cryptohopper config get | sed -E 's/("token": "[a-zA-Z0-9]{4})[a-zA-Z0-9]+/\1.../g'

If whoami --json returns the right user but a specific command (e.g. hoppers buy) returns FORBIDDEN, the issue is per-scope: your token has read but not trade, etc. Re-issue the token with broader scopes.

See also

  • Scripting — CI/automation, stable exit codes, jq pipelines, cron, Prometheus textfile
  • Troubleshooting — port-conflict errors, upgrade permission denials, UNAUTHORIZED diagnosis, shell-completion install
  • Public CLI reference (PR pending) — every subcommand, every flag, JSON output contract, exit-code table