capctl

June 15, 2026 · View on GitHub

An unofficial, fast, scriptable command-line client for the Capital.com Open API, built with Typer and Rich. Independent project — not affiliated with or endorsed by Capital.com.

Browse markets, manage accounts and watchlists, preview and execute trades behind multiple safety guardrails, and stream real-time prices — from your terminal, in human-readable tables or raw JSON for automation.

CI PyPI License: Apache 2.0 Python 3.10+ Release CLI + SDK coverage

Related: Capital.com MCP Server — the same broker engine exposed as a Model Context Protocol (MCP) server for LLM clients (Claude, Cursor, ChatGPT, …).

$ capctl market search "gold"
Markets
┏━━━━━━━━┳━━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━┓
┃ epic   ┃ instrumentName ┃ bid     ┃ offer   ┃ marketStatus ┃
┡━━━━━━━━╇━━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━┩
│ GOLD   │ Gold           │ 2331.05 │ 2331.35 │ TRADEABLE    │
└────────┴────────────────┴─────────┴─────────┴──────────────┘

New to this? The getting-started guide walks you from zero (no account, no API key) to your first practice trade on a demo account. Hit a problem? See troubleshooting.

Risk disclaimer: This is an unofficial tool, not affiliated with or endorsed by Capital.com, and nothing here is financial advice. CFD trading carries a high risk of losing money. The CLI defaults to the demo environment and disables trading until you explicitly opt in — keep it that way until you know exactly what you're doing. Use at your own risk.

Why capctl?

The Capital.com web platform is built for clicking; capctl is built for repeating, scripting, and automating. Anything you do through it can be put in a shell script, a cron job, or a pipeline — with machine-readable JSON output and exit codes you can branch on. And unlike ad-hoc API scripts, every action goes through the same safety layer: demo by default, trading off by default, an explicit market allowlist, size and daily-order limits, and a two-phase preview→execute flow that makes "oops, wrong size" structurally hard.

Who gets value from it:

  • Traders who live in the terminal — check positions, P&L, and orders in seconds without opening a browser; set a price alert and keep working.
  • People learning to trade — practice the full lifecycle (research → preview → execute → manage → close) on a demo account with virtual money, with guardrails on from the start. The getting-started guide assumes zero experience.
  • Developers building on the Capital.com API--json output mirrors the real API responses, so capctl doubles as an interactive API explorer while you build your own integration; the risk-engine pattern (allowlist, limits, preview/confirm) is reusable as a reference.
  • Data and automation folks — pull historical OHLC candles into CSV/pandas, log portfolio snapshots from cron, monitor markets from scripts, wire alerts into anything that can run a shell command.

See practical use cases for worked, copy-pasteable scenarios.

Scope: capctl is first and foremost a command-line application, but the same tested broker engine is also embeddable as a Python SDK — see Use as a library and docs/sdk.md. The capital_cli.core.* internals remain private and may change between releases. The CLI itself is short-lived: each command runs as its own process. By default (CAP_PERSIST_SESSION=true) the short-lived session tokens are cached so back-to-back commands reuse one login instead of re-authenticating each time (which can trip Capital.com's login-rate limit and return HTTP 429); capctl session login is mainly a connectivity/account check.

Features

  • Six command groups covering the full Capital.com Open API surface — session, market, account, trade, watchlist, stream — verified by an automated coverage matrix
  • Safety-first trading — trading is off by default; enabling it requires an explicit EPIC allowlist, every execution goes through a two-phase preview → execute flow with risk checks, and mutating commands require --yes
  • --json everywhere — every command can emit raw JSON for piping into jq, scripts, or CI jobs
  • Distinct exit codes per failure class — scripts can branch on why a command failed (auth vs. risk-block vs. upstream error)
  • Real-time streaming — live price tables, price-level alerts, and portfolio snapshots over WebSocket
  • Demo and live environments — defaults to demo; live requires explicit opt-in
  • Built-in rate limiting — client-side token buckets respect Capital.com's 10 req/s global, 1 req/s session, and trading-burst limits
  • Session-token caching — back-to-back commands reuse one login instead of re-authenticating (avoids HTTP 429); on by default, opt out with CAP_PERSIST_SESSION=false
  • Embeddable SDK — the same broker engine ships as an async Python library with a stable 0.x surface; see Use as a library

Installation

Requires Python 3.10+.

One-line install (recommended) — isolated, global capctl command:

pipx install capitalcom-cli

Using uv instead of pipx:

uv tool install capitalcom-cli

With Homebrew (macOS/Linux):

brew install SimonTarara62/tap/capctl

Plain pip (into the active environment):

pip install capitalcom-cli

To get the latest unreleased code instead of the last published release, replace the package name capitalcom-cli with git+https://github.com/SimonTarara62/capitalcom-cli.git in any command above.

From a clone (for development):

git clone https://github.com/SimonTarara62/capitalcom-cli.git
cd capitalcom-cli
python3 -m venv .venv && source .venv/bin/activate
pip install -e .

Verify and enable shell completion (bash/zsh/fish, provided by Typer):

capctl --version
capctl --install-completion

Shell completion

capctl ships completion for bash, zsh, and fish (via Typer):

capctl --install-completion        # install for your current shell
capctl --show-completion           # print the script (to inspect or source manually)

Restart your shell (or source your rc file) after installing.

Configuration

Generate an API key in the Capital.com platform under Settings → API integrations, then:

cp .env.example .env
# edit .env and fill in CAP_API_KEY, CAP_IDENTIFIER, CAP_API_PASSWORD

Credentials are resolved in this order:

  1. --env-file PATH flag
  2. $CAP_ENV_FILE environment variable
  3. ./.env in the current directory
  4. ~/.config/capital-cli/.env

Main settings and their defaults:

VariableDefaultPurpose
CAP_ENVdemodemo or live
CAP_API_KEYAPI key (required)
CAP_IDENTIFIERLogin email (required)
CAP_API_PASSWORDAPI-key custom password (required)
CAP_API_KEY_CMD(none)Command whose stdout supplies CAP_API_KEY (see below)
CAP_IDENTIFIER_CMD(none)Command whose stdout supplies CAP_IDENTIFIER
CAP_API_PASSWORD_CMD(none)Command whose stdout supplies CAP_API_PASSWORD
CAP_ALLOW_TRADINGfalseMaster switch for all trade execution
CAP_ALLOWED_EPICS(empty)Comma-separated allowlist, or ALL
CAP_MAX_POSITION_SIZE1.0Per-trade size ceiling
CAP_MAX_WORKING_ORDER_SIZE1.0Per-order size ceiling
CAP_MAX_OPEN_POSITIONS3Open-position cap
CAP_MAX_ORDERS_PER_DAY20Daily order counter
CAP_REQUIRE_EXPLICIT_CONFIRMtrueMutations need --yes
CAP_DRY_RUNfalseBlock all executions regardless of other flags
CAP_DEFAULT_ACCOUNT_ID(none)Account selected after login
CAP_PERSIST_SESSIONtrueCache short-lived session tokens in the state file so back-to-back commands reuse one login (avoids HTTP 429); set false to keep tokens in-process only
CAP_HTTP_TIMEOUT_S15HTTP timeout
CAP_LOG_LEVELWARNINGDEBUGCRITICAL
CAP_WS_ENABLEDfalseRequired for capctl stream …
CAP_AUDIT_LOG(none)File path; appends one JSONL line per executed mutation (no secrets)

Advanced/internal settings (rarely changed; safe defaults):

VariableDefaultPurpose
CAP_PREVIEW_CACHE_TTL_S120How long a preview_id stays valid before expiry
CAP_PING_INTERVAL_S480Session keep-alive ping interval

Credential-exec helpers (CAP_*_CMD)

To keep secrets out of plaintext files, source each credential from a command at runtime — the AWS credential_process / git-credential-helper pattern. Set CAP_API_KEY_CMD, CAP_IDENTIFIER_CMD, and/or CAP_API_PASSWORD_CMD to a command line; its trimmed stdout becomes the corresponding secret.

export CAP_API_PASSWORD_CMD="op read op://Private/capital/api-password"
export CAP_API_KEY_CMD="pass capital/api-key"

Precedence (highest first): an explicit CAP_<FIELD> env var, then the CAP_<FIELD>_CMD output, then the value in your .env file. Commands run with shell=False, a 10-second timeout, and a non-zero exit / timeout / empty output raises a clear configuration error (exit 3). The resolved secret is never logged and the command's stdout/stderr is never echoed in error messages. This is at-rest hygiene; see SECURITY.md for the threat model.

Trade previews and the daily order counter persist between commands in ~/.config/capital-cli/state.json (override with CAPCTL_STATE_FILE).

Global flags

These go before the command group:

FlagPurpose
--jsonEmit raw JSON instead of tables
--plainTab-delimited rows for piping (no boxes/colors)
--no-colorDisable colored output (also honors NO_COLOR)
--demo / --liveForce environment for this invocation
--env-file PATHUse a specific credentials file
--account ID, -aUse a specific account
--verbose, -vDebug logging (incl. per-command timing)
--versionPrint version and exit

Command reference

Session

capctl session status            # local session state (no network call)
capctl session details           # server-side session info (client/account ids, timezone)
capctl session time              # broker server time (no auth)
capctl session encryption-key    # API encryption key for encrypted-password login
capctl session login [--force] [--account ID]
capctl session ping              # keep the session alive
capctl session switch ACCOUNT_ID
capctl session logout

Note: commands that need authentication log in automatically; an explicit login is rarely required.

Market data

capctl market search "bitcoin" [--limit 20]
capctl market search --epics GOLD,SILVER
capctl market get GOLD                          # details + dealing rules
capctl market nav-root                          # top-level categories
capctl market nav-node NODE_ID                  # drill into a category
capctl market prices GOLD --resolution HOUR --max 48
capctl market sentiment GOLD                    # client long/short %
capctl market sentiment GOLD,SILVER,BTCUSD      # batch sentiment for several markets
capctl market nav-node NODE_ID --limit 50       # cap the number of children returned

Price resolutions: MINUTE, MINUTE_5, MINUTE_15, MINUTE_30, HOUR, HOUR_4, DAY, WEEK.

Account

capctl account list
capctl account prefs-get
capctl account prefs-set --hedging --yes        # risk-gated
capctl account prefs-set --leverage CRYPTOCURRENCIES=2 --leverage CURRENCIES=20 --yes
capctl account history-activity --last 3600
capctl account history-transactions --last 86400 [--type DEPOSIT]
capctl account topup 1000 --yes                 # demo environment only

Trading

Read-only:

capctl trade positions [--limit 10]              # -n/--limit caps rows shown
capctl trade position DEAL_ID
capctl trade orders [--limit 10]                 # -n/--limit caps rows shown
capctl trade confirm DEAL_REFERENCE [--wait --timeout 30]

Two-phase execution — preview first (validates against the risk policy and broker dealing rules, creates nothing), then execute with the returned preview_id:

capctl trade preview-position GOLD BUY 0.5 --stop-distance 10 --profit-distance 20
# → returns preview_id, risk-check table, estimated entry

capctl trade execute-position <preview_id> --yes

Working orders follow the same shape:

capctl trade preview-order GOLD BUY LIMIT 2300 0.5 --good-till 2026-07-01T00:00:00
capctl trade execute-order <preview_id> --yes

Closing and cancelling:

capctl trade close DEAL_ID --yes
capctl trade cancel DEAL_ID --yes

Amending an open position or a pending order (stops, limits, level, expiry):

capctl trade amend-position DEAL_ID --stop-level 2300 --profit-level 2450 --yes
capctl trade amend-order DEAL_ID --level 2310 --good-till 2026-08-01T00:00:00 --yes

Previews expire after 120 seconds. Execution commands wait for broker confirmation by default (--no-wait to skip; --timeout to tune).

Watchlists

capctl watchlist list
capctl watchlist get WATCHLIST_ID
capctl watchlist create "Metals" --yes
capctl watchlist add WATCHLIST_ID GOLD --yes
capctl watchlist remove WATCHLIST_ID GOLD --yes
capctl watchlist delete WATCHLIST_ID --yes

Streaming (requires CAP_WS_ENABLED=true)

capctl stream prices GOLD,SILVER,EURUSD --duration 120     # live updating table
capctl stream candles GOLD,BTCUSD --resolution MINUTE_5         # live OHLC candlesticks
capctl stream candles BTCUSD --resolution HOUR --type heikin-ashi
capctl stream alerts GOLD 2400 --direction ABOVE           # beep when crossed
capctl stream portfolio --duration 300 --interval 5        # snapshots for open positions

Streams stop after --duration seconds or Ctrl-C. Capital.com allows at most 40 concurrent EPIC subscriptions.

Use cases

Quick recipes below; for fuller worked scenarios (paper-trading practice, data analysis, cron monitoring, safe automation, API exploration) see docs/use-cases.md.

Morning check — one screen of context:

capctl account list && capctl trade positions && capctl trade orders

Scripting with --json and jq — extract the GOLD bid:

capctl --json market search --epics GOLD | jq -r '.markets[0].bid'

Export 200 daily candles to a CSV:

capctl --json market prices GOLD --resolution DAY --max 200 \
  | jq -r '.prices[] | [.snapshotTime, .openPrice.bid, .highPrice.bid, .lowPrice.bid, .closePrice.bid] | @csv' \
  > gold_daily.csv

Safe trade in the demo environment, end to end:

# .env: CAP_ENV=demo, CAP_ALLOW_TRADING=true, CAP_ALLOWED_EPICS=GOLD
PREVIEW=$(capctl --json trade preview-position GOLD BUY 0.5 --stop-distance 15 | jq -r .preview_id)
capctl trade execute-position "$PREVIEW" --yes
capctl trade positions

Branch on failure class in a script:

capctl --json trade execute-position "$PREVIEW" --yes
case $? in
  0) echo "filled" ;;
  4) echo "blocked by safety policy (trading disabled / confirm / risk limit)" ;;
  5) echo "auth problem — check credentials" ;;
  7) echo "broker/upstream error — retry later" ;;
esac

Watch for a breakout while you work:

capctl stream alerts BTCUSD 75000 --direction ABOVE --duration 3600

Cron-driven snapshot (JSON logs, no tables):

*/15 * * * * capctl --json trade positions >> ~/logs/positions.jsonl 2>&1

Use as a library

Beyond the CLI, the same broker engine — risk policy, two-phase preview→execute, rate limiting, WebSocket streaming — is available as an embeddable async SDK. CLI = terminal, scripts, CI; SDK = embed the tested engine in your own Python (MCP servers, dashboards, n8n nodes).

from capital_cli.sdk import CapitalComApp, CapitalComConfig, RiskPolicy
import asyncio
from capital_cli.sdk import CapitalComApp

async def main():
    async with CapitalComApp() as app:
        gold = await app.markets.get("GOLD")
        print(gold["snapshot"]["bid"])

asyncio.run(main())

Stability: the documented SDK surface — CapitalComApp, CapitalComConfig, RiskPolicy, the service classes, and the capital_cli.core.models pydantic models — is stable: no breaking changes within the 0.x series without a deprecation cycle; breaking changes are reserved for a future 1.0. The capital_cli.core.* internals remain private. See docs/sdk.md for read-only, preview→execute, streaming, and risk-policy examples.

Exit codes

CodeMeaning
0Success
1Unexpected internal error
2Invalid input (bad parameter or failed validation)
3Configuration error (missing/invalid credentials)
4Safety policy block (trading disabled, dry-run, confirm required, EPIC not allowed, risk limit)
5Authentication/session failure
6Local rate limit exceeded
7Broker / upstream API error
8Preview not found, expired, or failed checks
130Interrupted (Ctrl-C)

Safety model

Trade execution must pass all of these gates, in order:

  1. CAP_ALLOW_TRADING=true — master switch (default off)
  2. CAP_DRY_RUN=false — dry-run blocks everything
  3. --yes on the command (when CAP_REQUIRE_EXPLICIT_CONFIRM=true)
  4. A valid, unexpired preview whose risk checks all passed:
    • EPIC is in CAP_ALLOWED_EPICS
    • size within CAP_MAX_POSITION_SIZE, normalized to the broker's min/max/increment dealing rules
    • daily order counter under CAP_MAX_ORDERS_PER_DAY

There is no way to skip the preview step for positions and working orders — execute-* commands only accept a preview_id.

Live trading: keep CAP_ENV=demo until your workflow is proven. Switching to live requires editing .env (or passing --live) and having trading enabled — the defaults protect you twice.

Architecture

capital_cli/
├── core/        # Capital.com services: config, HTTP client, session,
│                # rate limiter, risk engine, models, errors, WebSocket
└── cli/         # presentation: Typer apps per command group,
                 # Rich/JSON output, async runner with exit-code mapping

The cli/ layer never talks to the API directly — every command parses arguments, calls a core service, and renders the result. All risk validation lives in core/risk.py, so safety rules cannot be bypassed by output or argument handling.

Development

pip install -e ".[dev]"
pytest -q              # full test suite
ruff check capital_cli tests   # lint
mypy capital_cli       # type-check the whole package (matches CI)

An opt-in end-to-end suite runs against the real demo API (credentials required):

CAPCTL_E2E=1 pytest tests/e2e -m e2e -v

Tests mock the HTTP and WebSocket layers — no network or credentials needed.

Or use the task runner: make check (lint + typecheck + test), make docs, make e2e.

Documentation

License

Apache License 2.0 — see LICENSE and NOTICE.

Licensed under the Apache License, Version 2.0. You may obtain a copy at http://www.apache.org/licenses/LICENSE-2.0. Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND.