Backtest-to-Live Deployment

SkillCloud & infra

Transition from backtest to live trading with zero code changes. Use when deploying a validated strategy to paper or live trading via broker APIs.

Available today. Use it from your connected AI after setup.

Add ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.

Then ask your AI: use the Backtest-to-Live Deployment skill

What this skill tells your AI

The instructions your AI receives, as published by ml4t/skills in production/live-trading/SKILL.md and read by ahel’s review.

Rewriting strategy logic for live trading introduces bugs and invalidates your backtest. The correct pattern is to reuse the exact Strategy class from backtesting - zero code changes between simulation and production.

The Problem

Teams often validate a strategy in backtest, then rewrite it for live trading. That rewrite changes rounding, timing, or position tracking and silently breaks the link to the validated backtest.

The Pattern

WRONG

# Separate live strategy - rewrites logic, diverges from backtest
class LiveMomentumTrader:
    def __init__(self, api_key):
        self.api = BrokerAPI(api_key)

    def run(self):
        while True:
            prices = self.api.get_latest_bars(100)
            signal = prices["close"].pct_change(20).iloc[-1]
            if signal > 0:
                self.api.market_buy("SPY", 100)  # Different sizing logic
            elif signal < 0:
                self.api.market_sell("SPY", 100)  # No cost model
            time.sleep(60)

CORRECT

from abc import ABC, abstractmethod

class Strategy(ABC):
    """Single strategy class used for BOTH backtest and live."""

    @abstractmethod
    def on_data(self, timestamp, data, context, broker):
        ...

class Momentum(Strategy):
    def on_data(self, timestamp, data, context, broker):
        for sym, bar in data.items():
            mom = bar.get("momentum_20d", 0)
            pos = broker.get_position(sym)
            if mom > 0 and not pos:
                size = int(broker.get_cash() * 0.05 / bar["close"])
                broker.submit_order(sym, size)
            elif mom <= 0 and pos:
                broker.close_position(sym)

# Backtest: Engine(feed, Momentum(), config).run()
# Live:     await LiveEngine(Momentum(), broker, feed).run()
# Same class. Same logic. Different engine.

Deployment Sequence

  1. Backtest - validate with historical data, realistic costs
  2. Paper trade (minimum 4 weeks) - same code, live data, simulated fills
  3. Shadow mode - generate orders but don't execute; compare to paper
  4. Live with limits - small size, tight kill switch, full monitoring
  5. Scale up - increase size only after live metrics match paper

Never skip paper trading. If paper diverges materially from backtest, diagnose before going live.

Data Feed Differences

PropertyBacktestLive
Data arrivalInstant, completeStreaming, may lag
BarsAll presentBuild incrementally
FillsSimulated, next-barReal, partial, rejected
ClockJump bar to barReal-time wall clock

Guardrails

  • Identical Strategy class for backtest and live - if you change one, you broke the link
  • Paper trade period is mandatory, not optional - 4 weeks minimum for daily strategies
  • Kill switch must be active before any live order: max drawdown, max position, daily loss limit
  • Log every order submission, fill, and rejection - you will need the audit trail
  • Data staleness check: if last bar is older than 2x expected frequency, halt trading

Production Implementation

import asyncio
from ml4t.backtest import Strategy
from ml4t.live import LiveEngine, AlpacaBroker, AlpacaDataFeed, SafeBroker, LiveRiskConfig

risk = LiveRiskConfig(execution_mode="shadow", max_drawdown_pct=0.10)
broker = SafeBroker(AlpacaBroker(api_key, secret_key), risk)
feed = AlpacaDataFeed(api_key, secret_key, symbols=["SPY"], experimental=True)

async def trade_live():
    engine = LiveEngine(Momentum(), broker, feed)
    await engine.connect()
    await engine.run()

asyncio.run(trade_live())

Checklist

  • Strategy class is identical for backtest and live (no separate live code)
  • Paper traded for minimum 4 weeks with live data
  • Kill switch configured with pre-approved thresholds
  • Partial fill handling verified
  • Data staleness detection active; order audit log captures every submission and fill

Signals

GitHub stars
20
Forks
11
Last commit
Sep 2026
Advanced
Item type
skill
Key
ml4t-live-trading
Source
github.com/ml4t/skills