Keel Docs
Getting Started

Your First Backtest (CLI)

Run a Keel backtest from the terminal with the current compose and summarize workflow.

Your First Backtest (CLI)

This walkthrough uses only the terminal. You will write a local Keel DSL file, validate it with keel strategy compose --dry-run, save it to the platform, run a backtest, and summarize the result.

See the finished thing first: Verified Keel backtest of a funding-carry strategy - Sharpe 2.17, +79.6% return, -9.7% max drawdown.

Prerequisites

  • Python 3.11+
  • A Keel account (sign up)
  • jq for piping JSON between commands

1. Install

pipx install keel-trade
keel --version
# keel, version 0.4.2

2. Authenticate

keel auth login

This opens your browser, runs OAuth 2.1 + PKCE against app.usekeel.io, and caches tokens in ~/.keel/config.yaml.

For CI, SSH, Codespaces, or WSL without browser forwarding:

keel auth login --key <token>
# Get a token at https://app.usekeel.io/settings?tab=api-keys

Verify:

keel --format json auth whoami
keel status --format json

3. Create strategy.py

cat > strategy.py <<'PY'
Globals(target_timeframe="1d")

Universe(mode="top_volume", top_n=30, market="perp", resolved=[], resolved_at="")

Execution(rebalance="every_bar")

Pipeline([
    PriceDataLoader(timeframe="15min"),
    TargetTimeframeResampler(),
    Store("ohlcv_1d"),
    FundingDataLoader(),
    SignalResampler(method="mean"),
    NegateTransform(),
    CrossSectionalZScore(),
    VolatilityStandardizer(signal_type="percentage", ohlcv_slot="ohlcv_1d"),
    ForecastScaler(avg_abs_target=10.0),
    ForecastCapper(limit=20.0),
    ForecastWeightNormalizer(target_leverage=1.0),
], name="my_carry")
PY

That file is Keel DSL: a typed component graph that loads price and funding data, turns funding into a carry forecast, scales risk, caps forecasts, and normalizes to target weights.

4. Validate without saving

keel strategy compose --source-file strategy.py --dry-run --format json \
  | jq '.validation'

dry_run checks the source and returns structured validation feedback without creating or updating a strategy.

5. Save the strategy

STRATEGY_ID=$(keel strategy compose \
  --source-file strategy.py \
  --name my_carry \
  --format json | jq -r '.strategy_id')
echo "$STRATEGY_ID"
# str_01HX...

keel strategy compose replaces the old create/validate command split. Use --strategy-id <id> later to update an existing platform strategy from a local file.

6. Run a backtest

BT_ID=$(keel backtest run "$STRATEGY_ID" \
  --start-date 2025-06-01 \
  --format json | jq -r '.run_id')
echo "$BT_ID"
# btr_01HX...

keel backtest run waits by default for up to about 90 seconds and uses today's UTC date when --end-date is omitted. If the run is still in progress, pass the run_id to keel backtest watch:

keel backtest watch "$BT_ID" --format json

7. Summarize the result

keel backtest summarize "$BT_ID" --format json | jq '.summary_metrics'

Open the returned hero_url in your browser for the full tearsheet.

What is next?

  • Iterate locally: edit strategy.py, then run keel strategy compose --strategy-id "$STRATEGY_ID" --source-file strategy.py --dry-run.
  • Push an update: rerun compose without --dry-run, or use the checkout flow with keel strategy checkout, keel strategy status, and keel strategy push.
  • Share the result: keel share create "$BT_ID" --target-type backtest.
  • Preview live deployment: keel live deploy "$STRATEGY_ID" --account-id <account-id>. Actual deployment requires keel auth login --scope live, keel arm live set --account <account-id>, and then an explicit --no-preview.