Skip to content
Ronald Hinchley edited this page Mar 8, 2026 · 7 revisions

CLI — Task Guide

ibctl is the command-line interface to a running TWS Headless engine. It communicates over a Unix socket (/tmp/tws_headless.sock by default).

./ibctl.py [command] [args...] [--socket PATH] [--timeout SECS] [--json]

Table of Contents


How do I see what's going on?

Account and portfolio overview

./ibctl.py status
./ibctl.py summary
./ibctl.py summary --json        # machine-readable

All open positions

./ibctl.py positions

All loaded plugins

./ibctl.py plugin list

A specific plugin's positions and open orders

./ibctl.py plugin dump momentum_5day
./ibctl.py plugin dump momentum_5day --json

Plugin internal state

./ibctl.py plugin status momentum_5day

How do I fund a plugin?

Funding a plugin means moving cash (or positions) from the _unassigned pool into the plugin's ledger. No IB orders are placed — this is bookkeeping only.

Step 1 — See what's available in the unassigned pool

./ibctl.py transfer list _unassigned

Step 2 — Preview the transfer (no --confirm)

./ibctl.py transfer cash _unassigned momentum_5day 25000

The engine shows what would change without making any changes.

Step 3 — Execute the transfer

./ibctl.py transfer cash _unassigned momentum_5day 25000 --confirm

Fund with an existing position instead of cash

# Preview
./ibctl.py transfer position _unassigned momentum_5day SPY 100

# Execute
./ibctl.py transfer position _unassigned momentum_5day SPY 100 --confirm

Fund with both cash and a position

Run two transfers in sequence — one for cash, one for each position.

Return funds to the pool

Transfer in the opposite direction:

./ibctl.py transfer cash momentum_5day _unassigned 25000 --confirm

How do I transfer assets between plugins?

Transfers between any two plugins work the same as transfers to/from _unassigned.

Transfer cash

# From plugin A to plugin B
./ibctl.py transfer cash plugin_a plugin_b 5000 --confirm

Transfer a position

./ibctl.py transfer position plugin_a plugin_b SPY 50 --confirm

See what a plugin holds that can be transferred

./ibctl.py transfer list plugin_a

Notes

  • Both plugins must be loaded (not necessarily started)
  • The source must have sufficient cash/quantity
  • Negative amounts are not allowed — reverse the from/to instead
  • No confirmation prompt is shown without --confirm; always preview first

How do I load and start a plugin?

Load a plugin

./ibctl.py plugin load plugins.momentum_5day.plugin

The path is a Python import path relative to the engine's working directory.

Load with a named slot (for multiple instances)

Append =slot_name to the path to assign a stable instance key. Each slot gets independent state, holdings, and a distinct CLI address:

./ibctl.py plugin load plugins.momentum_5day.plugin=spy_momentum
./ibctl.py plugin load plugins.momentum_5day.plugin=qqq_momentum

# Now address each by slot name
./ibctl.py plugin start spy_momentum
./ibctl.py plugin start qqq_momentum
./ibctl.py transfer cash _unassigned spy_momentum 25000 --confirm
./ibctl.py transfer cash _unassigned qqq_momentum 25000 --confirm

Without =slot, the plugin's own name (from super().__init__("name", ...)) is used as the slot.

With an optional descriptor (metadata tag visible in plugin list):

./ibctl.py plugin load plugins.momentum_5day.plugin "SPY momentum strategy"

Start a loaded plugin

./ibctl.py plugin start momentum_5day

You can also use the plugin's UUID (shown in plugin list):

./ibctl.py plugin start a3f1c2d4-...

Load and start in one go

plugin load automatically transitions through LOADED; you still need plugin start to move to STARTED. The typical sequence is:

./ibctl.py plugin load plugins.momentum_5day.plugin
./ibctl.py plugin start momentum_5day
./ibctl.py transfer cash _unassigned momentum_5day 25000 --confirm

Check it's running

./ibctl.py plugin list
./ibctl.py plugin status momentum_5day

How do I stop or pause a plugin?

Freeze (pause without stopping)

The plugin saves its state, calculate_signals stops being called, but streams and subscriptions remain active. Resume any time.

./ibctl.py plugin freeze momentum_5day
./ibctl.py plugin resume momentum_5day

Stop a plugin

The plugin saves state, unsubscribes from streams and MessageBus, and transitions to STOPPED. Holdings are preserved.

./ibctl.py plugin stop momentum_5day

Unload a plugin

Stops (if running) then removes it from the engine. Holdings remain in SQLite for the next load.

./ibctl.py plugin unload momentum_5day

Disable signal generation without stopping

The plugin keeps running (receiving data, callbacks fire) but calculate_signals is skipped.

./ibctl.py plugin disable momentum_5day
./ibctl.py plugin enable momentum_5day

Pause all plugins at once

./ibctl.py pause
./ibctl.py resume

How do I place a manual trade?

Simple market order

./ibctl.py order buy  SPY 100
./ibctl.py order sell QQQ  50 --confirm

Limit order

./ibctl.py order buy  AAPL 25 limit 175.00 --confirm
./ibctl.py order sell MSFT 30 limit 420.00

Stop order

./ibctl.py order sell SPY 100 stop 445.00 --confirm

Stop-limit order

# stop price, then limit price
./ibctl.py order sell SPY 100 stop-limit 445 443 --confirm

Trailing stop — fixed amount

./ibctl.py order sell SPY 100 trail 2.50 --confirm

Trailing stop — percentage

./ibctl.py order sell SPY 100 trail 0.5% --confirm

Market on Close / Market on Open

./ibctl.py order buy SPY 100 moc --confirm
./ibctl.py order buy SPY 100 moo --confirm

Limit on Close / Limit on Open

./ibctl.py order sell QQQ 50 loc 390.00 --confirm
./ibctl.py order buy  QQQ 50 loo 385.00 --confirm

Time in force

Append --tif to any order type:

./ibctl.py order buy SPY 100 limit 450 --tif gtc --confirm   # Good-til-cancelled
./ibctl.py order buy SPY 100 limit 450 --tif ioc             # Immediate-or-cancel
./ibctl.py order buy SPY 100 limit 450 --tif fok             # Fill-or-kill

Default TIF is day.

Trade as a plugin instance

Use trade instead of order to place a real IB order and book the fill against a specific plugin's holdings, commission tracking, and P&L ledger.

# By plugin name (works when only one instance is loaded)
./ibctl.py trade momentum_5day BUY SPY 50 --confirm

# By slot name (required when multiple instances of the same class are running)
./ibctl.py trade spy_momentum BUY SPY 100 --confirm
./ibctl.py trade qqq_momentum BUY QQQ 75  --confirm

# By UUID (always unambiguous)
./ibctl.py trade a3f1c2d4-17e7-4ee5-ac03-282e0cd05c2b BUY SPY 50 --confirm

# With a reason (logged in execution history)
./ibctl.py trade spy_momentum SELL SPY 50 --reason "stop hit" --confirm

The fill updates that instance's holdings (current_cash, current_positions), commissions are attributed to it, and P&L is reported against it — identical to a signal the plugin generated itself.

Without --confirm, the command is a dry-run preview that shows what would be placed without placing it.

Liquidate positions

./ibctl.py liquidate SPY --confirm        # One symbol
./ibctl.py liquidate --confirm            # Everything

How do I send a custom request to a plugin?

Plugins implement handle_request(request_type, payload). You can invoke it from the CLI:

# No payload
./ibctl.py plugin request momentum_5day get_status

# With JSON payload
./ibctl.py plugin request momentum_5day set_period '{"period": 20}'

# Get the response as JSON
./ibctl.py plugin request momentum_5day get_status --json

The response always has a "success" key. On success, data is under "data".

General-purpose messaging

Use plugin message to send arbitrary JSON to a plugin without specifying a request type. Inside the plugin, request_type will be "message":

./ibctl.py plugin message momentum_5day '{"action": "reset", "value": 0.5}'

Get a plugin's CLI help

Well-behaved plugins implement cli_help() to document their commands:

./ibctl.py plugin help momentum_5day

Trigger one execution cycle manually

Useful for testing calculate_signals without waiting for the next bar:

./ibctl.py plugin trigger momentum_5day

How do I reconcile plugin holdings with IB?

Reconciliation compares what each plugin's ledger says it holds against the actual IB account positions.

./ibctl.py reconcile
./ibctl.py reconcile --json      # Machine-readable report

The report shows:

  • Positions the plugin claims but IB doesn't have
  • Positions IB has that no plugin claims
  • Cash discrepancies

Reconciliation is advisory — it reports differences but does not auto-correct them. Use transfers to realign bookkeeping.


How do I shut down gracefully?

./ibctl.py stop

This signals the engine to:

  1. Stop all plugins (saving state)
  2. Cancel open IB subscriptions
  3. Close the IB connection
  4. Exit

Full Command Reference

Connection options (all commands)

Flag Default Description
--socket PATH /tmp/tws_headless.sock Unix socket path
--timeout SECS 10 Command timeout
--json off Force JSON output

Status & information

Command Description
status Account overview and net liquidation
positions All positions with P&L
summary Plugin-by-plugin breakdown
help List all commands

Plugin management

Command Description
plugin list All loaded plugins, their state, instance ID
plugin load PATH[=SLOT] [DESC] Load plugin; optional =SLOT assigns instance key
plugin unload NAME|SLOT|ID Unload plugin (stops first if needed)
plugin status NAME|SLOT|ID Plugin state, holdings summary
plugin start NAME|SLOT|ID Transition LOADED → STARTED
plugin stop NAME|SLOT|ID Transition STARTED/FROZEN → STOPPED
plugin freeze NAME|SLOT|ID Pause signal generation, keep streams
plugin resume NAME|SLOT|ID Resume from FROZEN
plugin enable NAME|SLOT|ID Re-enable signal generation
plugin disable NAME|SLOT|ID Suppress signals without stopping
plugin trigger NAME|SLOT|ID Run one calculate_signals cycle now
plugin dump NAME|SLOT|ID Positions, open orders, holdings detail
plugin request NAME TYPE [JSON] Send typed request to plugin's handle_request
plugin message NAME [JSON] Send arbitrary JSON (delivers request_type="message")
plugin help NAME Show plugin CLI help (calls cli_help())

NAME is the plugin's class name; SLOT is the instance key assigned at load; ID is the UUID shown in plugin list. All are accepted interchangeably.


Transfers

Command Description
transfer list PLUGIN Show cash and positions available to transfer
transfer cash FROM TO AMOUNT [--confirm] Move cash between plugins
transfer position FROM TO SYMBOL QTY [--confirm] Move position between plugins

Without --confirm, all transfer commands are dry-run previews.


Orders

Command Description
order buy|sell SYMBOL QTY [TYPE] [options] [--confirm] Place any order type
trade NAME|SLOT|ID buy|sell SYMBOL QTY [--confirm] [--reason TEXT] Order attributed to a plugin instance; updates its holdings and P&L
liquidate [SYMBOL] [--confirm] Liquidate one or all positions

Order types:

Type syntax IB order type
(omitted) Market (MKT)
limit PRICE Limit (LMT)
stop PRICE Stop (STP)
stop-limit STOP LIMIT Stop-Limit (STP LMT)
trail AMOUNT Trailing Stop — fixed
trail N% Trailing Stop — percentage
moc Market on Close
loc PRICE Limit on Close
moo Market on Open
loo PRICE Limit on Open

TIF values: day (default) · gtc · ioc · fok


System control

Command Description
pause Freeze all plugins
resume Resume all plugins
reconcile [--json] Compare plugin ledgers to IB account
stop Graceful engine shutdown

TWS Headless


Theory of Operation

  • Startup sequence
  • Market data & streams
  • Plugin execution
  • Holdings & bookkeeping
  • Order lifecycle
  • State persistence

CLI — Task Guide


Plugin Manual ← complete reference

Bar Store

Plugin Design

  • File layout
  • Lifecycle methods
  • State persistence
  • Market data streams
  • Trade signals
  • Order callbacks
  • Holdings management
  • MessageBus
  • ContractBuilder
  • Instrument compliance
  • Multiple instances (slots)
  • CLI help & messaging
  • Threading rules
  • Full example

Clone this wiki locally