Go from idea to implementation in minutes, not weeks or months.






import backtrader as bt
import datetime
import numpy as np
import matplotlib.pyplot as plt
class SMAMeanReversion(bt.Strategy):
"""
Simple SMA Mean Reversion Strategy
- Calculates 20-day Simple Moving Average (SMA)
- Enters long position when price closes 2% below SMA
- Position size: 0.5% of current account balance
- Uses limit order at close price
- Adds to position if price drops another 1% from initial entry
- Stop loss: 1% below initial entry price
- Take profit: when price reaches the 20-day SMA
"""
params = (
('sma_period', 20),
('entry_threshold', 0.02), # 2% below SMA
('add_threshold', 0.01), # Additional 1% drop from entry
('position_size_pct', 0.005), # 0.5% of account balance
('stop_loss_pct', 0.01), # 1% below entry
)
def __init__(self):
# Calculate 20-day SMA
self.sma = bt.indicators.SimpleMovingAverage(
self.data.close, period=self.params.sma_period
)
# Track initial entry price and position size
self.initial_entry_price = None
self.total_position_size = 0
def next(self):
# Check if we have a position
if not self.position:
# No position: check entry condition
if self.data.close[0] < self.sma[0] * (1 - self.params.entry_threshold):
# Calculate position size: 0.5% of current account value
account_value = self.broker.getvalue()
position_value = account_value * self.params.position_size_pct
size = position_value / self.data.close[0]
# Place limit order at close price
self.buy(
size=size,
price=self.data.close[0],
exectype=bt.Order.Limit
)
# Record initial entry
self.initial_entry_price = self.data.close[0]
self.total_position_size = size
else:
# Have position: check for adding to position
if (self.data.close[0] < self.initial_entry_price *
(1 - self.params.add_threshold)):
# Price dropped another 1% from initial entry
account_value = self.broker.getvalue()
position_value = account_value * self.params.position_size_pct
size = position_value / self.data.close[0]
# Add to position with limit order
self.buy(
size=size,
price=self.data.close[0],
exectype=bt.Order.Limit
)
self.total_position_size += size
# Check exit conditions
# Take profit: price reaches SMA
if self.data.close[0] >= self.sma[0]:
self.sell(size=self.total_position_size)
self.initial_entry_price = None
self.total_position_size = 0
# Stop loss: 1% below initial entry
elif (self.data.close[0] <= self.initial_entry_price *
(1 - self.params.stop_loss_pct)):
self.sell(size=self.total_position_size)
self.initial_entry_price = None
self.total_position_size = 0
def notify_order(self, order):
if order.status in [order.Completed]:
if order.isbuy():
print(f'BUY EXECUTED: {order.executed.price:.2f}, Size: {order.executed.size:.2f}')
elif order.issell():
print(f'SELL EXECUTED: {order.executed.price:.2f}, Size: {order.executed.size:.2f}')
def calculate_kelly_criterion(returns, risk_free_rate=0.03):
"""Calculate Kelly Criterion"""
if len(returns) == 0:
return 0
# Annualize returns
daily_returns = np.array(returns)
mean_return = np.mean(daily_returns)
variance = np.var(daily_returns)
if variance == 0:
return 0
# Kelly formula: (mean - risk_free) / variance
kelly = (mean_return - risk_free_rate / 252) / variance # Assuming daily data
return max(0, kelly) # Don't go negative
def calculate_buy_and_hold_return(data):
"""Calculate buy and hold return"""
if len(data) < 2:
return 0
initial_price = data[0]
final_price = data[-1]
return (final_price - initial_price) / initial_price
if __name__ == '__main__':
# Create a cerebro instance
cerebro = bt.Cerebro()
# Add the strategy
cerebro.addstrategy(SMAMeanReversion)
# Load AAPL data from compressed CSV
aapl_data_path = 'data/aapl_daily_2020_2023.csv.gz'
try:
data = bt.feeds.GenericCSVData(
dataname=aapl_data_path,
dtformat='%Y-%m-%d',
datetime=0,
open=1,
high=2,
low=3,
close=4,
volume=5,
openinterest=-1,
fromdate=datetime.datetime(2020, 1, 1),
todate=datetime.datetime(2023, 1, 1)
)
cerebro.adddata(data, name='AAPL')
print(f"Loaded AAPL data from {aapl_data_path}")
except FileNotFoundError:
print(f"Warning: {aapl_data_path} not found. Using sample Yahoo data for demo.")
# Fallback to Yahoo data for demonstration
data = bt.feeds.YahooFinanceData(
dataname='AAPL',
fromdate=datetime.datetime(2020, 1, 1),
todate=datetime.datetime(2023, 1, 1)
)
cerebro.adddata(data, name='AAPL')
# Load benchmark data (S&P 500) from compressed CSV
spx_data_path = 'data/spx_daily_2020_2023.csv.gz'
try:
benchmark_data = bt.feeds.GenericCSVData(
dataname=spx_data_path,
dtformat='%Y-%m-%d',
datetime=0,
open=1,
high=2,
low=3,
close=4,
volume=5,
openinterest=-1,
fromdate=datetime.datetime(2020, 1, 1),
todate=datetime.datetime(2023, 1, 1)
)
cerebro.adddata(benchmark_data, name='SPX')
print(f"Loaded SPX benchmark data from {spx_data_path}")
except FileNotFoundError:
print(f"Warning: {spx_data_path} not found. Using sample Yahoo data for demo.")
# Fallback to Yahoo data for demonstration
benchmark_data = bt.feeds.YahooFinanceData(
dataname='^SPX',
fromdate=datetime.datetime(2020, 1, 1),
todate=datetime.datetime(2023, 1, 1)
)
cerebro.adddata(benchmark_data, name='SPX')
# Set initial cash
initial_cash = 10000.0
cerebro.broker.setcash(initial_cash)
# Add comprehensive analyzers
cerebro.addanalyzer(bt.analyzers.Returns, _name='returns')
cerebro.addanalyzer(bt.analyzers.SharpeRatio, _name='sharpe', riskfreerate=0.03)
cerebro.addanalyzer(bt.analyzers.DrawDown, _name='drawdown')
cerebro.addanalyzer(bt.analyzers.SQN, _name='sqn')
cerebro.addanalyzer(bt.analyzers.VWR, _name='vwr')
cerebro.addanalyzer(bt.analyzers.TimeReturn, _name='timereturn')
cerebro.addanalyzer(bt.analyzers.AlphaBeta, _name='alphabeta', benchmark='SPX')
cerebro.addanalyzer(bt.analyzers.AnnualReturn, _name='annualreturn')
print(f'Starting Portfolio Value: ${cerebro.broker.getvalue():.2f}')
# Run the backtest
results = cerebro.run()
strat = results[0]
final_value = cerebro.broker.getvalue()
print(f'Final Portfolio Value: ${final_value:.2f}')
# Extract data for buy and hold calculation
aapl_data = data.close.array
buy_hold_return = calculate_buy_and_hold_return(aapl_data)
print(f'Buy and Hold Return: {buy_hold_return:.2%}')
# Performance Metrics
print('\n=== PERFORMANCE METRICS ===')
# Total Return
total_return = strat.analyzers.returns.get_analysis()['rtot']
print(f'Total Return: {total_return:.2%}')
# CAGR (Compound Annual Growth Rate)
cagr = strat.analyzers.returns.get_analysis()['rnorm']
print(f'CAGR: {cagr:.2%}')
# Annual Volatility
# Calculate from daily returns
daily_returns = list(strat.analyzers.timereturn.get_analysis().values())
if daily_returns:
ann_vol = np.std(daily_returns) * np.sqrt(252) # Assuming 252 trading days
print(f'Annual Volatility: {ann_vol:.2%}')
# Sharpe Ratio
sharpe = strat.analyzers.sharpe.get_analysis()['sharperatio']
print(f'Sharpe Ratio: {sharpe:.2f}')
# Sortino Ratio (Downside deviation)
if daily_returns:
downside_returns = [r for r in daily_returns if r < 0]
if downside_returns:
downside_dev = np.std(downside_returns) * np.sqrt(252)
sortino = (cagr - 0.03) / downside_dev if downside_dev > 0 else 0
print(f'Sortino Ratio: {sortino:.2f}')
# SQN (System Quality Number)
sqn = strat.analyzers.sqn.get_analysis()['sqn']
print(f'SQN: {sqn:.2f}')
# Calmar Ratio
max_dd = strat.analyzers.drawdown.get_analysis()['max']['drawdown']
calmar = cagr / (max_dd / 100) if max_dd > 0 else 0
print(f'Calmar Ratio: {calmar:.2f}')
# Kelly Criterion
kelly = calculate_kelly_criterion(daily_returns)
print(f'Kelly Criterion: {kelly:.2%}')
# Alpha and Beta
alpha_beta = strat.analyzers.alphabeta.get_analysis()
alpha = alpha_beta['alpha']
beta = alpha_beta['beta']
print(f'Alpha: {alpha:.2%}')
print(f'Beta: {beta:.2f}')
# Maximum Drawdown
print(f'Maximum Drawdown: {max_dd:.2%}')
# Annual Returns
annual_returns = strat.analyzers.annualreturn.get_analysis()
print('\nAnnual Returns:')
for year, ret in annual_returns.items():
print(f' {year}: {ret:.2%}')
# Plot the results
print('\nGenerating performance chart...')
try:
fig = cerebro.plot(style='candlestick', volume=False, savefig=True, figfilename='backtest_results.png')
print('Chart saved as backtest_results.png')
except Exception as e:
print(f'Could not generate chart: {e}')
print('\nBacktest completed successfully!')Strategies can access OHLC and Trade data from multiple timeframes and instruments at once, enabling comprehensive multi-timeframe, multi-instrument analysis and execution.
Stratifyre's trading engine comes with hundreds of built-in indicators that you can use to create and customize your strategies. From classic indicators like EMA, VWAP, and Volume Profile to Dragonfly Dojis or SMC / ICT models, the possibilities are endless.
Stratifyre's trading engine uses dynamically generated and evaluated code to execute your strategies, giving you unparalleled flexibility. You're not limited to pre-defined templates or rigid rule sets.
Strategies can store and access internal state across trades and time periods. Track variables like trade counts, cumulative profits, or custom flags to create sophisticated logic that adapts to market conditions.
Everything you need to go from trading idea to live algorithm — without writing a single line of code.
Describe your strategy in plain English. The AI writes the logic, picks the indicators, and configures the rules.
No Python. No APIs. No quant degree. Your trading knowledge is the only prerequisite.
Change a parameter, tweak a rule, swap an indicator — then backtest again. No rebuild, no rewrite.
No servers, no data feeds, no DevOps. We handle the terabytes of market data and compute.
Cloud-hosted execution, real-time data pipelines, and automatic scaling — all included. Focus purely on strategy logic.
The same AI-powered strategy creation tools used by quantitative firms — now available to every trader.

No coding. No hassle.
Just describe your strategy in plain English and start trading.