StratLab
Describe your strategy
Enter any trading idea in the left panel and we will simulate it against real Yahoo Finance historical data.
Run a backtest first
Monte Carlo stress-tests your strategy across 1,000 alternative market scenarios.
No trades yet
Each simulated trade will appear here with real Yahoo Finance entry and exit prices.
Comparison
Compare Strategies
Select saved strategies and run them side by side over the current date range and capital.
Saved strategies
Options Lab
Multi-Leg Options Backtester
Black-Scholes pricing, Greeks, and an expiration-path backtest for verticals, condors, straddles and more.
Volatility proxy
Structure
Python Script
Broker-Ready Strategy Code
Auto-generated. Connects to Interactive Brokers, Alpaca, or any broker with a Python API. Edit only the CONFIG block to deploy live.
# StratLab - Strategy Engine - Auto-Generated
# Compatible: Alpaca, Interactive Brokers, Binance, CCXT
# Data: Yahoo Finance (yfinance) - edit CONFIG only
import pandas as pd, numpy as np, yfinance as yf
from dataclasses import dataclass
from typing import List
import warnings; warnings.filterwarnings("ignore")
CONFIG = {
"tickers": ["SPY", "QQQ", "AAPL"], "start": "2018-01-01", "end": "2024-12-31",
"interval": "1d", "capital": 100_000, "position_pct": 0.10,
"stop_loss_pct": 0.02, "take_profit_pct": 0.04, "commission_bps": 2,
"max_dd_halt": 0.20, "ema_fast": 9, "ema_slow": 21,
"rsi_period": 14, "rsi_entry": 50, "rsi_exit": 70,
}
@dataclass
class Trade:
ticker: str; direction: str; entry_date: str; exit_date: str
entry_price: float; exit_price: float; pnl: float; pnl_pct: float; reason: str
class Engine:
def __init__(self, cfg):
self.cfg=cfg; self.equity=cfg["capital"]; self.peak=self.equity
self.trades: List[Trade]=[]; self.curve=[self.equity]; self.halted=False
def ema(self,s,n): return s.ewm(span=n,adjust=False).mean()
def rsi(self,s,n=14):
d=s.diff(); g=d.clip(0).rolling(n).mean(); l=(-d).clip(0).rolling(n).mean()
return 100-100/(1+g/l.replace(0,np.nan))
def run(self,ticker):
df=yf.download(ticker,start=self.cfg["start"],end=self.cfg["end"],
interval=self.cfg["interval"],progress=False,auto_adjust=True)
if df.empty: return []
c=self.cfg
df["ef"]=self.ema(df["Close"],c["ema_fast"]); df["es"]=self.ema(df["Close"],c["ema_slow"])
df["rsi"]=self.rsi(df["Close"],c["rsi_period"]); df.dropna(inplace=True)
in_trade=False; entry=None
for i in range(1,len(df)):
row,prev=df.iloc[i],df.iloc[i-1]
if self.halted: break
if not in_trade:
if prev["ef"]<prev["es"] and row["ef"]>=row["es"] and row["rsi"]>c["rsi_entry"]:
entry,in_trade=row,True
else:
pct=(row["Close"]-entry["Close"])/entry["Close"]; reason=""
if pct<=-c["stop_loss_pct"]: reason="Stop Loss"
elif pct>=c["take_profit_pct"]: reason="Take Profit"
elif row["rsi"]>=c["rsi_exit"]: reason="RSI Exit"
if reason:
net=(pct-2*c["commission_bps"]/10000)*self.equity*c["position_pct"]
self.equity+=net
if self.equity>self.peak: self.peak=self.equity
if 1-self.equity/self.peak>=c["max_dd_halt"]: self.halted=True
self.trades.append(Trade(ticker,"LONG",str(entry.name)[:10],str(row.name)[:10],
float(entry["Close"]),float(row["Close"]),net,pct*100,reason))
self.curve.append(self.equity); in_trade=False
return self.trades
class AlpacaBroker:
def connect(self,key,secret,base="https://paper-api.alpaca.markets"): pass
def order(self,sym,qty,side="buy"): pass
class IBKRBroker:
def connect(self,host="127.0.0.1",port=7497): pass
def order(self,sym,qty,side="BUY"): pass
if __name__=="__main__":
engine=Engine(CONFIG)
for t in CONFIG["tickers"]:
trades=engine.run(t); wr=sum(1 for x in trades if x.pnl>0)/max(len(trades),1)
print(f"{t}: {len(trades)} trades | WR {wr:.1%} | ${engine.equity:,.0f}")