Skip to Content
cTrader MarketsDeployment

Deployment

ops/ has one launchd plist per profile plus ops/install.sh, which is the supported install path — it creates the logs/ and data/ directories launchd cannot create for itself, and refuses to install an env file that still holds template placeholders or a port already in use.

UnitProfilePortEnv file
com.ctrader-markets.forexforex8010.env.forex
com.ctrader-markets.derivderiv8011.env.deriv

Run exactly one process per broker account. Two processes on the same credentials mean two sessions and two token-refresh races.

Install a profile

python3.12 -m venv .venv .venv/bin/python -m pip install -e '.[dev]' cp .env.example.forex .env.forex # then fill it in ./ops/install.sh forex

ops/install.sh is the supported path because it does the things whose absence is invisible:

  • creates logs/ and data/, which are gitignored and absent from a fresh checkout. launchd creates the log file but not its parent directory, so without this the redirect silently fails and a crash loop leaves no trace.
  • refuses to install while the env file still holds replace-with-… placeholders.
  • refuses a port already in use.
  • refuses two profiles sharing a TOKEN_CACHE_PATH.

The plists hardcode absolute paths, because launchd expands neither ~ nor a shell environment. Edit them if the checkout moves.

Health

curl -s localhost:8010/health/ready | jq .details

/health/live answers as soon as the process is up. /health/ready returns 503 until the broker session is connected and a quote has arrived within TICK_STALENESS_SECONDS, and its details carry state, reconnects and last_error.

tail -f logs/forex.log # stdout, JSON per line jq -r .event logs/events.forex.jsonl | sort | uniq -c # the durable record

Watch for ctrader_connect_failed, access_token_rejected, access_token_invalidated and stream_subscriber_lagging.

Restart and removal

launchctl kickstart -k gui/$(id -u)/com.ctrader-markets.forex # restart launchctl print gui/$(id -u)/com.ctrader-markets.forex # state, exit code launchctl bootout gui/$(id -u)/com.ctrader-markets.forex # stop and unload rm ~/Library/LaunchAgents/com.ctrader-markets.forex.plist # uninstall

A restart is always safe: the service re-authenticates, reloads the symbol catalog and re-subscribes from configuration alone. Nothing is resumed from disk except the token pair.

Failure modes

SymptomLikely causeCheck
logs/forex.log empty, process restarting every 60scrash before logging — bad env file, port in use, missing venvrun .venv/bin/ctrader-markets --profile forex in the foreground
/health/ready 503, last_error mentions CH_CLIENT_AUTH_FAILUREwrong CTRADER_CLIENT_ID / CTRADER_CLIENT_SECRETre-check the application page at openapi.ctrader.com
503 with symbol_resolution_failed in the loga name in SYMBOLS is not exposed by this broker--discover-symbols, copy exact symbolName values
repeated access_token_rejected then silencerefresh token expired or already rotated elsewhereredo the OAuth flow, then --refresh-token
ready but no ticks on a weekdaysymbols resolve but the market is closed, or the account has no feedcompare against a cTrader chart
stream_subscriber_lagging in the events logan SSE consumer is too slow; oldest ticks are being dropped for itthe dropped counter on that subscriber’s status events

ThrottleInterval of 60s stops a crash loop from hammering the broker. Reconnect backoff grows to RECONNECT_MAX_BACKOFF_SECONDS and only resets once a connection has stayed up for RECONNECT_STABILITY_SECONDS.

Nothing rotates logs/*.log; they grow unbounded. Add a newsyslog.d drop-in with size-based rotation and the J (compress) flag — launchd holds the file open, so do not move files out from under the process. See ops/README.md.

Last updated on