Command-line client for the Capital.com Open API
Project description
capctl — Capital.com CLI
A fast, scriptable command-line client for the Capital.com Open API, built with Typer and Rich.
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.
$ 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 —
--jsonoutput mirrors the real API responses, socapctldoubles 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 an experimental 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 main Capital.com Open API workflows —
session,market,account,trade,watchlist,stream(see the API coverage table) - 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 --jsoneverywhere — every command can emit raw JSON for piping intojq, 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 (experimental) — the same broker engine ships as an async Python library; 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
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:
--env-file PATHflag$CAP_ENV_FILEenvironment variable./.envin the current directory~/.config/capital-cli/.env
Main settings and their defaults:
| Variable | Default | Purpose |
|---|---|---|
CAP_ENV |
demo |
demo or live |
CAP_API_KEY |
— | API key (required) |
CAP_IDENTIFIER |
— | Login email (required) |
CAP_API_PASSWORD |
— | API-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_TRADING |
false |
Master switch for all trade execution |
CAP_ALLOWED_EPICS |
(empty) | Comma-separated allowlist, or ALL |
CAP_MAX_POSITION_SIZE |
1.0 |
Per-trade size ceiling |
CAP_MAX_WORKING_ORDER_SIZE |
1.0 |
Per-order size ceiling |
CAP_MAX_OPEN_POSITIONS |
3 |
Open-position cap |
CAP_MAX_ORDERS_PER_DAY |
20 |
Daily order counter |
CAP_REQUIRE_EXPLICIT_CONFIRM |
true |
Mutations need --yes |
CAP_DRY_RUN |
false |
Block all executions regardless of other flags |
CAP_DEFAULT_ACCOUNT_ID |
(none) | Account selected after login |
CAP_PERSIST_SESSION |
true |
Cache 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_S |
15 |
HTTP timeout |
CAP_LOG_LEVEL |
WARNING |
DEBUG … CRITICAL |
CAP_WS_ENABLED |
false |
Required 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):
| Variable | Default | Purpose |
|---|---|---|
CAP_PREVIEW_CACHE_TTL_S |
120 |
How long a preview_id stays valid before expiry |
CAP_PING_INTERVAL_S |
480 |
Session 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:
| Flag | Purpose |
|---|---|
--json |
Emit raw JSON instead of tables |
--plain |
Tab-delimited rows for piping (no boxes/colors) |
--no-color |
Disable colored output (also honors NO_COLOR) |
--demo / --live |
Force environment for this invocation |
--env-file PATH |
Use a specific credentials file |
--account ID, -a |
Use a specific account |
--verbose, -v |
Debug logging (incl. per-command timing) |
--version |
Print 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 (experimental)
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())
The SDK is experimental until 1.0: import paths and the pydantic models are intended-stable but may shift between 0.x minors. See docs/sdk.md for read-only, preview→execute, streaming, and risk-policy examples.
Exit codes
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Unexpected internal error |
| 2 | Invalid input (bad parameter or failed validation) |
| 3 | Configuration error (missing/invalid credentials) |
| 4 | Safety policy block (trading disabled, dry-run, confirm required, EPIC not allowed, risk limit) |
| 5 | Authentication/session failure |
| 6 | Local rate limit exceeded |
| 7 | Broker / upstream API error |
| 8 | Preview not found, expired, or failed checks |
| 130 | Interrupted (Ctrl-C) |
Safety model
Trade execution must pass all of these gates, in order:
CAP_ALLOW_TRADING=true— master switch (default off)CAP_DRY_RUN=false— dry-run blocks everything--yeson the command (whenCAP_REQUIRE_EXPLICIT_CONFIRM=true)- 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
- EPIC is in
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
- Full CLI reference — every command and option (auto-generated)
- Using capctl as a Python SDK — experimental async SDK, import paths, examples, versioning
- Scripting & automation — CI credentials, exit codes, jq recipes
- Getting started from zero — account, API key, install, first trade
- Practical use cases — what people actually do with capctl, with copy-pasteable scenarios
- Troubleshooting — common errors and exit codes
- Contributing — dev setup and conventions
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.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file capitalcom_cli-0.5.1.tar.gz.
File metadata
- Download URL: capitalcom_cli-0.5.1.tar.gz
- Upload date:
- Size: 90.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fdeb4c893e85adca58943666b037f4d4c7f8e92403e143fa30293f574fa797bf
|
|
| MD5 |
3b56c65e8c1e49d4d5267f3c117a2122
|
|
| BLAKE2b-256 |
6784674993a6a75ad38894660eddfb8cb9ee7853632e67a3a2cf854c51bd98b7
|
Provenance
The following attestation bundles were made for capitalcom_cli-0.5.1.tar.gz:
Publisher:
release.yml on SimonTarara62/capitalcom-cli
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
capitalcom_cli-0.5.1.tar.gz -
Subject digest:
fdeb4c893e85adca58943666b037f4d4c7f8e92403e143fa30293f574fa797bf - Sigstore transparency entry: 1818731649
- Sigstore integration time:
-
Permalink:
SimonTarara62/capitalcom-cli@c4ab8863d0eebaeab8090a520bb74460f2fd27a4 -
Branch / Tag:
refs/tags/v0.5.1 - Owner: https://github.com/SimonTarara62
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@c4ab8863d0eebaeab8090a520bb74460f2fd27a4 -
Trigger Event:
push
-
Statement type:
File details
Details for the file capitalcom_cli-0.5.1-py3-none-any.whl.
File metadata
- Download URL: capitalcom_cli-0.5.1-py3-none-any.whl
- Upload date:
- Size: 75.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
97c6b024bbd6478d3a2df2e20ab645f2322c6cf38926a936be401b4197d9416f
|
|
| MD5 |
c2c403bf6f989dd12c27fe7924c101d9
|
|
| BLAKE2b-256 |
8b1f1e86d5c0f1caff53caed18f808a5a3d2d3b2b911989c67a3f5516a7aeb86
|
Provenance
The following attestation bundles were made for capitalcom_cli-0.5.1-py3-none-any.whl:
Publisher:
release.yml on SimonTarara62/capitalcom-cli
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
capitalcom_cli-0.5.1-py3-none-any.whl -
Subject digest:
97c6b024bbd6478d3a2df2e20ab645f2322c6cf38926a936be401b4197d9416f - Sigstore transparency entry: 1818731719
- Sigstore integration time:
-
Permalink:
SimonTarara62/capitalcom-cli@c4ab8863d0eebaeab8090a520bb74460f2fd27a4 -
Branch / Tag:
refs/tags/v0.5.1 - Owner: https://github.com/SimonTarara62
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@c4ab8863d0eebaeab8090a520bb74460f2fd27a4 -
Trigger Event:
push
-
Statement type: