Live Trading Guide

May 6, 2026 ยท View on GitHub

Warning

DISCLAIMER: This software is for educational and research purposes only. Trading involves significant risk of loss and is not suitable for all investors. Use of "Live Trading" features is strictly at your own risk. The authors and contributors are not liable for any financial losses, damages, or unintended trades incurred. Always test strategies thoroughly in a paper-trading environment before deploying real capital.

The Trading Bot Framework can mirror your paper-trade portfolios (stored in PostgreSQL) to a live brokerage account. This is handled by a separate Live Trade Copier layer that runs independently of your bots.

Caution

ALPHA STATUS: The live-trade copier is currently in Alpha. While endpoints have been validated against broker APIs (C2 v4, IB, eToro, Darwinex), behavior has not yet been confirmed against a live capital account. Use extreme caution and start with very low weights.


๐Ÿš€ Quick Start (5-Minute Dry Run)

You can verify the copier logic immediately without any complex setup.

  1. Set Environment Variables:
    export DARWINEX_USERNAME="your_username"
    export DARWINEX_PASSWORD="your_password"
    export LIVETRADE_BOT_WEIGHTS='{"adaptivemeanreversionbot": 1.0}'
    export LIVETRADE_DRY_RUN=true
    
  2. Run the Copier:
    uv run python tradingbot/livetrade_darwinex.py
    
  3. Review the Log: Look for [DRY RUN] Would BUY/SELL ... lines to see what the copier would have done.

๐Ÿ”Ž Inspect Account & Positions

Each broker module is runnable as a script and prints a summary of the configured account โ€” equity, cash, and current open positions. Use it to sanity-check credentials and account IDs before running the copier:

# Collective2 โ€” reads COLLECTIVE2_API_KEY + COLLECTIVE2_SYSTEM_ID
uv run python tradingbot/livetrade/collective2.py

# Interactive Brokers โ€” reads IB_GATEWAY_HOST/PORT + IB_ACCOUNT_ID
# Connects read-only with IB_CLIENT_ID=19 by default so it won't collide
# with the cron client (17) or the vscode debug config (18).
uv run python tradingbot/livetrade/interactive_brokers.py

# eToro โ€” reads ETORO_API_KEY + ETORO_USER_KEY + ETORO_DEMO
uv run python tradingbot/livetrade/etoro.py

# Darwinex โ€” reads DARWINEX_USERNAME + DARWINEX_PASSWORD + DARWINEX_DEMO
uv run python tradingbot/livetrade/darwinex.py

All brokers expose print_account_summary() on the broker class, so you can call it from any script after constructing the broker.


๐Ÿ“Š How Orders are Calculated

The copier does not just "blindly" copy signals; it synchronizes target state:

  1. Per-Bot Normalization: For each bot in LIVETRADE_BOT_WEIGHTS, the current paper portfolio is converted to percentage weights (e.g., AAPL is 10% of Bot A).
  2. Weighted Aggregation: Individual bot weights are aggregated based on your allocation (e.g., if Bot A is 60% of your capital, AAPL becomes 6% of your live total).
  3. Broker Equity Sync: The copier fetches your Live Total Equity (Cash + Open Positions) from the broker.
  4. Target Value: (Total Equity) ร— (Aggregate Target Weight) = Target USD Value for each symbol.
  5. The Diff: The copier compares the Target Value vs. your Current Live Position value at the broker.
  6. Order Generation: It generates the BUY or SELL orders needed to close the gap.
  7. Safety Filters: Orders smaller than LIVETRADE_MIN_ORDER_USD are skipped to avoid excessive fees.

Price Fallback: If the broker cannot provide a real-time quote for a symbol, the framework falls back to yfinance to ensure calculations remain accurate.

Multi-Bot Weighting Example

If you have $100,000 in your live account and configure: LIVETRADE_BOT_WEIGHTS='{"adaptivemeanreversionbot": 0.6, "feargreedbot": 0.4}'

  1. Bot A (Adaptive): Paper portfolio is 100% QQQ.
  2. Bot B (FearGreed): Paper portfolio is 50% QQQ, 50% CASH.
  3. Aggregation:
    • QQQ Target = (1.0 ร— 0.6) + (0.5 ร— 0.4) = 0.8 (80%)
    • CASH Target = (0.0 ร— 0.6) + (0.5 ร— 0.4) = 0.2 (20%)
  4. Final Target: The copier will try to make your live account hold $80,000 of QQQ.

๐Ÿ›  Configuration Reference

VariableDescription
COLLECTIVE2_API_KEYYour C2 API v4 key. Get it from the C2 API Dashboard.
COLLECTIVE2_SYSTEM_IDThe ID of your C2 strategy.
IB_GATEWAY_HOSTHostname or IP of IB Gateway (default: 127.0.0.1).
IB_GATEWAY_PORTAPI port for IB Gateway (default: 4004).
IB_CLIENT_IDUnique client ID for the copier (default: 17). Do not use Master Client ID (0).
IB_ACCOUNT_IDYour Interactive Brokers account ID (e.g., DU1234567 for paper, U1234567 for live).
ETORO_API_KEYYour eToro Public API Key from the eToro API Portal.
ETORO_USER_KEYYour eToro User Key (generated in API Portal settings).
ETORO_DEMOtrue for demo/paper account, false for live account (default: true).
DARWINEX_USERNAMEYour Darwinex DXtrade username.
DARWINEX_PASSWORDYour Darwinex DXtrade master password.
DARWINEX_ACCOUNT_IDOptional: Specific Darwinex account ID to use.
DARWINEX_DEMOtrue for demo/paper account, false for live account (default: true).
LIVETRADE_BOT_WEIGHTSJSON: {"botname": 0.6, "otherbot": 0.4}. Weights are normalized to 1.0.
LIVETRADE_MIN_ORDER_USDSkip trades smaller than this amount (default: $50).
LIVETRADE_DRY_RUNtrue: Logs orders without sending them. Always start here.
LIVETRADE_STRICT_MAPPINGtrue: Aborts the sync if any target ticker is unmapped, instead of silently skipping it.
LIVETRADE_PORTFOLIO_FRACTIONFraction of broker equity to allocate to copy-trading. Default 1.0 (use the full account); 0.5 would mirror the bot portfolios into half the account and leave the rest as cash. Range: (0, 1].

Enabling the Copiers in Helm

Each broker is its own CronJob, gated by a separate flag in helm/tradingbots/values.yaml. All default to false โ€” opt in independently:

liveTrade:
  enabled: true     # Collective2 copier
  # ...
liveTradeIB:
  enabled: true     # Interactive Brokers copier
  # ...
liveTradeEToro:
  enabled: true     # eToro copier
  # ...
liveTradeDarwinex:
  enabled: true     # Darwinex copier
  # ...

You can run any combination of brokers (Collective2, IBKR, eToro, Darwinex), or none.


๐Ÿฆ Interactive Brokers (IBKR)

The framework supports Interactive Brokers via IB Gateway (or TWS).

1. Requirements

  • IB Gateway running and configured for API access.
  • Paper Account strongly recommended for initial testing.
  • Paper Port: Usually 4002 (standard) or 4004 (often used in local setups).

2. Configuration

Set these environment variables:

VariableDescriptionDefault
IB_GATEWAY_HOSTHostname of IB Gateway.127.0.0.1
IB_GATEWAY_PORTAPI Port.4004
IB_CLIENT_IDUnique ID for this connection.17
IB_ACCOUNT_IDYour IB account ID (e.g., DU1234567).Required
LIVETRADE_DRY_RUNPaper-safety: defaults to true.true

Important

clientId and Order Isolation: The copier is designed to be idempotent. Before each sync, it calls cancel_open_orders() to clear any stale orders submitted by previous runs that haven't filled yet. To ensure this does not cancel your manual orders, the copier only targets orders with a matching IB_CLIENT_ID.

  • Always use a unique IB_CLIENT_ID (default: 17) for the copier.
  • Avoid using "Master Client ID" (0) for the copier, as it can see and cancel orders from all other clients.

3. Usage

uv run python tradingbot/livetrade_interactive_brokers.py

4. Ticker Discovery for IB

uv run python -m tradingbot.livetrade.discover_symbols --broker ib

๐Ÿ‚ eToro

The framework supports eToro via their Public REST API.

1. Requirements

  • eToro Public API Key & User Key. Obtain them from the eToro API Portal.
  • Paper Account (Virtual Portfolio) is strongly recommended. Set ETORO_DEMO=true.

2. Configuration

Set these environment variables:

VariableDescriptionDefault
ETORO_API_KEYYour eToro API Key.Required
ETORO_USER_KEYYour eToro User Key.Required
ETORO_DEMOtrue for paper trading, false for live.true
LIVETRADE_DRY_RUNSafety: defaults to true.true

3. Order Model (USD Amount)

eToro orders are placed using USD Amount (notional) for BUY orders, which allows for fractional shares automatically. For SELL orders, the framework closes existing positions by their positionId.

4. Ticker Mapping

eToro uses numeric Instrument IDs. The framework automatically resolves these via the eToro search API during the mapping phase.

5. Usage

uv run python tradingbot/livetrade_etoro.py

๐Ÿ“ˆ Darwinex (DXtrade)

The framework supports Darwinex via the DXtrade REST API (/dxsca-web).

1. Requirements

  • Darwinex DXtrade Account (Username + Master Password).
  • Demo Account strongly recommended for initial testing.

2. Configuration

Set these environment variables:

VariableDescriptionDefault
DARWINEX_USERNAMEYour DXtrade username.Required
DARWINEX_PASSWORDYour DXtrade master password.Required
DARWINEX_DEMOtrue for demo/paper trading, false for live.true
DARWINEX_ACCOUNT_IDOptional: defaults to the first account found.None

3. Symbol Mapping (CFDs)

Darwinex is a CFD broker. Common equity tickers like QQQ are often mapped to QQQ.US. The framework uses a two-step resolution:

  1. Check symbol_mappings.json for manual overrides.
  2. Search the Darwinex catalog for the ticker (and ticker + .US).

4. Native Quotes

DXtrade REST API does not provide a simple "last price" endpoint (it is WebSocket only). For v1, the framework falls back to yfinance for real-time price lookups to calculate equity and order sizing.

5. Usage

uv run python tradingbot/livetrade_darwinex.py

๐Ÿ” Ticker Mapping (Discovery)

Broker symbols (e.g., EURUSD) rarely match yfinance tickers (EURUSD=X) exactly.

1. Run Discovery

Find unmapped tickers in your bot portfolios:

uv run python -m tradingbot.livetrade.discover_symbols

2. Review and Approve

Open symbol_map.review.json. For each ticker, manually add the selected_symbol and selected_type keys:

"BTC-USD": {
  "candidates": [...],
  "selected_symbol": "BTCUSD",  // YOU ADD THIS
  "selected_type": "crypto"     // YOU ADD THIS
}

3. Apply

uv run python -m tradingbot.livetrade.discover_symbols --apply

๐Ÿ›ก Going Live Checklist

  1. Dry Run: Run for at least 3-5 days with LIVETRADE_DRY_RUN=true.
  2. Verify Equity: Confirm the "Broker total equity" logged matches your broker dashboard.
  3. Strict Mode: Set LIVETRADE_STRICT_MAPPING=true.
  4. Start Small: Use one bot with a low weight (e.g., 0.1) first.
  5. Manual Check: After the first live sync, verify the orders appear in your broker's "Open Orders" or "Positions" tab.

๐Ÿ— Deployment (Kubernetes)

Deploy the copier as a CronJob that runs after your trading bots finish.

apiVersion: batch/v1
kind: CronJob
metadata:
  name: livetrade-copier
spec:
  schedule: "5 21 * * 1-5" # 9:05 PM UTC (Post-market)
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: copier
            image: your-repo/tradingbot:latest
            command: ["python", "tradingbot/livetrade_collective2.py"]
            envFrom:
            - secretRef:
                name: tradingbot-secrets

๐Ÿ”Œ Adding a New Broker

To support a new broker, implement the LiveBroker interface in tradingbot/livetrade/broker.py:

  1. get_positions(): Return Dict[symbol, qty].
  2. get_total_equity(): Return float (Cash + Market Value).
  3. place_order(symbol, qty, side, type): Execute the trade.
  4. get_latest_price(symbol): Return current price for a broker symbol.
  5. map_symbol(yf_symbol): Convert yf ticker to broker ticker.
  6. search_symbol(query): Helper for ticker discovery.

Reference tradingbot/livetrade/collective2.py for a complete example.


โ“ Troubleshooting

IssueRoot CauseSolution
401/403 ErrorInvalid API KeyEnsure you are using a v4 key from the C2 dashboard.
Empty "Results"Wrong System IDVerify COLLECTIVE2_SYSTEM_ID matches your strategy URL.
"Order Failed" LogBroker RejectionCheck the log line for ErrorCode; usually due to buying power or invalid symbol.
All targets unmappedTypo or Empty BotVerify bot names in LIVETRADE_BOT_WEIGHTS match the DB exactly.
Quantity 0 warningMin Order FilterIncrease a specific bot's weight or decrease LIVETRADE_MIN_ORDER_USD.

๐Ÿšซ When NOT to Use

  • Before you have at least 1 month of paper trading history.
  • With a System ID that you do not own/manage.
  • If you require sub-second execution (this is a scheduled copier, not a HFT engine).