The Complete System Diagram
Every component we've built now connects. The live runner receives candles from the Phase 2 pipeline, runs them through the Phase 3 strategy stack, sizes positions, and emits order signals.
Live Ticks
OHLCV
SMA/EMA/RSI…
on_candle()
Sizer
Emitter
Strategy Runner Class
The StrategyRunner bridges Phase 2's live data pipeline with Phase 3's strategy stack. It accepts a candle from the pipeline and handles everything downstream.
import json, logging from datetime import datetime, date from pathlib import Path import pandas as pd from strategy_base import StrategyBase, Signal from position_sizer import PositionSizer from indicators import ema, rsi, vwap, supertrend from performance import PerformanceReport log = logging.getLogger("runner") class StrategyRunner: def __init__( self, strategy : StrategyBase, sizer : PositionSizer, equity : float = 100_000, paper_mode : bool = True, # set False to enable real orders ): self.strategy = strategy self.sizer = sizer self.equity = equity self.paper_mode = paper_mode self.orders : list[dict] = [] self._log_path = Path(f"logs/orders_{date.today()}.jsonl") Path("logs").mkdir(exist_ok=True) def on_candle(self, df: pd.DataFrame, symbol: str): """ Called by pipeline.on_candle() with enriched DataFrame. Runs strategy, sizes position, emits order. """ sig = self.strategy.on_candle(df) if sig.direction not in ("LONG", "SHORT"): return row = df.iloc[-1] entry = row["close"] atr = (df["high"] - df["low"]).rolling(14).mean().iloc[-1] # Stop & target (1.5×ATR / 3×ATR = 2:1 R:R) sl = entry - 1.5*atr if sig.direction=="LONG" else entry + 1.5*atr tgt = entry + 3.0*atr if sig.direction=="LONG" else entry - 3.0*atr # Size position lots = self.sizer.fixed_pct_risk( equity = self.equity, entry_price = entry, stop_price = sl, risk_pct = 1.0, ) order = { "ts" : row.name.isoformat(), "symbol" : symbol, "direction": sig.direction, "entry" : round(entry, 2), "sl" : round(sl, 2), "target" : round(tgt, 2), "lots" : lots, "reason" : sig.reason, "paper" : self.paper_mode, } self.orders.append(order) self._log_order(order) if not self.paper_mode: self._place_live_order(order) # Phase 4 will implement this else: log.info( f"[PAPER] {symbol} {sig.direction} {lots}L @ {entry:.0f} " f"SL={sl:.0f} TGT={tgt:.0f}" ) def _log_order(self, order: dict): with self._log_path.open("a") as f: f.write(json.dumps(order) + "\n") def _place_live_order(self, order: dict): """Stub — live order placement added in Phase 4.""" raise NotImplementedError("Live orders require Phase 4 broker integration") def session_summary(self): """Print session P&L from trade log.""" trades = self.strategy.trade_log() if len(trades) == 0: log.info("No completed trades this session.") return total = trades["pnl_pts"].sum() * 50 log.info( f"Session: {len(trades)} trades | " f"Net P&L ≈ ₹{total:,.0f}" )
Plugging Into the Phase 2 Pipeline
Update pipeline.py from Phase 2 to route candles through the strategy runner. Only 10 lines change.
from strategy_ema_cross import EMACrossStrategy from strategy_runner import StrategyRunner from position_sizer import PositionSizer # In Pipeline.__init__() add: self.runner = StrategyRunner( strategy = EMACrossStrategy(fast=9, slow=21), sizer = PositionSizer(lot_size=50), equity = 500_000, paper_mode = True, # ← set False when ready for live ) # In Pipeline.on_candle() after compute_indicators(): self.runner.on_candle(df, symbol) # In Pipeline.start() at end of session: self.runner.session_summary()
Run with paper_mode=True for at least 2 weeks of live market data. Compare paper results to your WFO OOS expectations. Only set paper_mode=False after results are consistent. Phase 4 will add actual broker order placement.
Before Going Live — Final Checklist
- Strategy WFO validated — OOS efficiency > 0.5 across at least 6 windows
- Cost model verified — backtest includes realistic slippage & brokerage
- Position sizing capped — max lots enforced, daily loss limit set
- Paper traded 2+ weeks — live paper results match backtest expectations
- Market hours kill switch — no orders outside 09:15–15:15 IST
- JSONL logging active — every signal logged for post-session review
Phase 3 Complete!
You've built a complete strategy development framework — from architecture and backtesting to position sizing, performance metrics, walk-forward validation, and a live runner.
Back to Learning Hub
