ml4t
live
Python

Live trading runtime for ML4T strategies with broker integrations, risk checks, and shadow mode.

Last updated Jul 8, 2026
12
Stars
9
Forks
0
Issues
0
Stars/day
Attention Score
63
Language breakdown
No language data available.
Files click to expand
README

ml4t-live

Python 3.12+ PyPI License: MIT

Live trading platform with zero-code migration from backtest to production.

Part of the ML4T Library Ecosystem

This library is one of six interconnected libraries supporting the machine learning for trading workflow described in Machine Learning for Trading:

ML4T Library Ecosystem

Together they cover data infrastructure, feature engineering, modeling, signal evaluation, strategy backtesting, and live deployment.

What This Library Does

Deploying a backtested strategy to live markets requires careful handling of async broker connections, risk limits, and testing infrastructure. ml4t-live provides:

  • The same Strategy class used in ml4t-backtest works unchanged in production
  • Two broker integrations: Interactive Brokers (TWS/Gateway) and Alpaca (stocks + crypto)
  • Six data feeds: Alpaca, IB, Databento, CCXT (100+ crypto exchanges), OKX
  • Shadow mode for testing without placing real orders (VirtualPortfolio tracking)
  • 16-parameter risk configuration: position limits, order limits, loss limits, price protection
  • Kill switch with crash-safe state persistence (atomic JSON writes)
  • Startup preflight, reconciliation, and JSONL execution journaling for operator workflows
  • Async architecture with thread-safe sync bridge for strategy callbacks
The goal is gradual deployment: shadow mode first, then paper trading, then live with small positions.

ml4t-live Architecture

Installation

uv add ml4t-live

Quick Start

from ml4t.backtest import Strategy, OrderSide
from ml4t.live import LiveEngine, LiveRiskConfig, SafeBroker
from ml4t.live.brokers.alpaca import AlpacaBroker
from ml4t.live.feeds.alpaca_feed import AlpacaDataFeed
import asyncio

Same strategy class from backtesting

class MyStrategy(Strategy): def on_data(self, timestamp, data, context, broker): if not broker.get_position('SPY'): broker.submit_order('SPY', 10, side=OrderSide.BUY)

async def main(): broker = AlpacaBroker(apikey="...", secretkey="...", paper=True) feed = AlpacaDataFeed(apikey="...", secretkey="...", symbols=["SPY"])

config = LiveRiskConfig( shadow_mode=True, # No real orders maxpositionvalue=50_000, ) safe = SafeBroker(broker, config)

engine = LiveEngine(MyStrategy(), safe, feed) await engine.connect()

try: await engine.run() finally: await engine.stop()

asyncio.run(main())

Broker Integrations

Alpaca

Stocks and crypto with paper trading by default:

from ml4t.live.brokers.alpaca import AlpacaBroker

broker = AlpacaBroker( api_key="...", secret_key="...", paper=True, # Paper trading (default) ) await broker.connect()

Interactive Brokers

Full market access via TWS or IB Gateway:

from ml4t.live.brokers.ib import IBBroker

broker = IBBroker(port=7497) # TWS paper port

broker = IBBroker(port=7496) # TWS live port

await broker.connect() print(f"Connected: {broker.is_connected}")

Requirements:

  • IB TWS or Gateway running
  • API connections enabled in TWS settings
  • Paper trading account for initial testing

Data Feeds

| Feed | Source | Coverage | |------|--------|----------| | AlpacaDataFeed | Alpaca | US stocks + crypto, real-time bars/quotes/trades | | IBDataFeed | Interactive Brokers | Multi-asset tick-by-tick data | | DataBentoFeed | Databento | Historical replay + real-time streaming | | CryptoFeed | CCXT | 100+ crypto exchanges (Binance, Coinbase, Kraken, ...) | | OKXFundingFeed | OKX | Perpetual swaps with funding rates | | BarAggregator | Any feed | Multi-feed aggregation + bar assembly |

from ml4t.live.feeds.alpaca_feed import AlpacaDataFeed
from ml4t.live.feeds.crypto_feed import CryptoFeed

Stock + crypto via Alpaca

feed = AlpacaDataFeed( apikey="...", secretkey="...", symbols=["AAPL", "BTC/USD"], feed="iex", # "iex" (free) or "sip" (premium) )

Crypto via CCXT (any of 100+ exchanges)

feed = CryptoFeed( exchange="binance", symbols=["BTC/USDT", "ETH/USDT"], timeframe="1m", )

Risk Configuration

LiveRiskConfig controls all safety parameters. Wrap any broker with SafeBroker to enforce them:

from ml4t.live import LiveRiskConfig, SafeBroker

config = LiveRiskConfig( # Shadow mode shadow_mode=True, # Virtual orders only (no real execution)

# Position limits maxpositionvalue=50_000, # Max $ per position maxpositionshares=1000, # Max shares per position maxtotalexposure=200_000, # Max total $ across all positions max_positions=20, # Max number of positions

# Order limits maxordervalue=10_000, # Max $ per order maxordershares=500, # Max shares per order maxordersper_minute=10, # Rate limiting

# Loss limits maxdailyloss=5_000, # Stop trading if exceeded maxdrawdownpct=0.05, # Stop if 5% drawdown

# Price protection maxpricedeviation_pct=0.05, # Fat finger: reject if >5% from market maxdatastaleness_seconds=60, # Reject if data older than 60s dedupwindowseconds=1.0, # Block duplicate orders within 1s

# Asset restrictions allowed_assets={"SPY", "QQQ"}, # Whitelist (empty = allow all)

# Startup and persistence failonreconciliation_mismatch=True, journalfile=".ml4texecution_journal.jsonl", )

safe_broker = SafeBroker(broker, config)

Safety System

Kill Switch

When drawdown exceeds maxdrawdownpct, the kill switch activates and blocks all new orders. The state persists across process restarts:

config = LiveRiskConfig(
    killswitchenabled=True,
    maxdrawdownpct=0.05,
    statefile=".ml4trisk_state.json",  # Atomic JSON writes
)

Virtual Portfolio

Shadow mode tracks positions internally without broker interaction:

from ml4t.live import VirtualPortfolio

portfolio = VirtualPortfolio(initialcash=100000)

SafeBroker uses this automatically when shadow_mode=True

State Persistence

Risk state survives process crashes via atomic file writes:

  • daily_loss - Cumulative daily loss
  • orders_placed - Orders placed today
  • highwatermark - Session high equity
  • killswitchactivated - Persists until manually reset
SafeBroker also writes a JSONL execution journal with reconciliation, order, kill-switch, and runtime health events. By default it sits next to the state file.

Operator CLI

Use the CLI as a thin operator surface around the Python API:

# Fail-fast startup check for a real broker session
uv run ml4t-live preflight ib --state-file .ml4triskstate.json --strict

Human-readable state and recent journal tail

uv run ml4t-live status --state-file .ml4triskstate.json

Bounded shadow soak

uv run ml4t-live shadow examples/shadowmodedemo.py --feed okx --duration 60

preflight is the beta-oriented command: it checks broker reachability, balances, persisted kill-switch state, startup reconciliation, and session state, and exits non-zero when the result is degraded.

Order Lifecycle

Strategies still place orders through the same synchronous wrapper interface, but pending orders can now be replaced in a normalized way:

def on_data(self, timestamp, data, context, broker):
    if broker.pending_orders:
        broker.replaceorder(broker.pendingorders[0].orderid, limitprice=189.5)

The default implementation uses a safe cancel-and-resubmit flow across supported brokers.

Deployment Progression

  • Shadow Mode (1-2 weeks): Verify logic without real orders
  • Paper Trading (2-4 weeks): Test with paper account
  • Live Micro (1-2 weeks): Small positions ($100-500)
  • Live Small (ongoing): Gradual size increase

Strategy Compatibility

The same Strategy class works in both environments:

from ml4t.backtest import Strategy

class MyStrategy(Strategy): def on_data(self, timestamp, data, context, broker): # This code runs identically in backtest and live pass

Backtest

from ml4t.backtest import Engine result = Engine(feed, MyStrategy(), config).run()

Live

from ml4t.live import LiveEngine await LiveEngine(MyStrategy(), safebroker, livefeed).run()

Documentation

Technical Characteristics

  • Async/sync bridge: Sync strategy callbacks work with async broker connections via ThreadSafeBrokerWrapper
  • Thread-safe: Strategy runs in worker thread, broker I/O on async event loop
  • Protocol-based: BrokerProtocol, AsyncBrokerProtocol, DataFeedProtocol for extensibility
  • Virtual portfolio: Shadow mode tracks positions without broker interaction
  • Atomic state: Risk state persisted via POSIX-atomic file writes (crash-safe)
  • Rate limiting: Built-in protection against order flooding
  • Type-safe: Full type annotations throughout

Related Libraries

  • ml4t-data: Market data acquisition and storage
  • ml4t-engineer: Feature engineering and technical indicators
  • ml4t-diagnostic: Signal evaluation and statistical validation
  • ml4t-backtest: Event-driven backtesting

Development

git clone https://github.com/ml4t/live.git
cd ml4t-live
uv sync
uv run pytest tests/ -q
uv run ty check

Safety Notice

This library is designed for paper trading and educational purposes. When transitioning to live trading:

  • Always start with shadow_mode=True
  • Set conservative position and order limits
  • Enable killswitchenabled=True with a reasonable maxdrawdownpct
  • Monitor virtual vs real positions carefully
  • Use the deployment progression above

License

MIT License - see LICENSE for details.

🔗 More in this category

© 2026 GitRepoTrend · ml4t/live · Updated daily from GitHub