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)
jqfor piping JSON between commands
1. Install
pipx install keel-trade
keel --version
# keel, version 0.4.22. Authenticate
keel auth loginThis 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-keysVerify:
keel --format json auth whoami
keel status --format json3. 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")
PYThat 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 json7. 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 runkeel 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 withkeel strategy checkout,keel strategy status, andkeel 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 requireskeel auth login --scope live,keel arm live set --account <account-id>, and then an explicit--no-preview.