Trade Like a Machine

Code for the chapter "Backtesting Without a PhD". Click Copy on any block and paste it straight into TradingView or QuantConnect. Results shown in the book come from AAPL, daily bars, 2019 through 2025. Backtests are hypothetical, and past results do not guarantee future results. Nothing here is investment advice.

Example 1. Three Down Days

The complete template. Paste the Pine Script into a new strategy in TradingView's Pine Editor. Paste the C# into a new C# algorithm in QuantConnect, replacing everything in the file. Then click Backtest.

Pine Script (TradingView)
//@version=6
strategy("Three Down Days", overlay=true, default_qty_type=strategy.percent_of_equity, default_qty_value=100)
startDate = timestamp("2019-01-01")
endDate = timestamp("2025-12-31")
inWindow = time >= startDate and time <= endDate
qqqClose = request.security("QQQ", "D", close)
qqqEma50 = ta.ema(qqqClose, 50)
qqqEma200 = ta.ema(qqqClose, 200)
marketOK = qqqEma50 > qqqEma200
threeDown = close < close[1] and close[1] < close[2] and close[2] < close[3]
signal = marketOK and threeDown and inWindow
var int entryBar = na
if signal and strategy.position_size == 0
    strategy.entry("Long", strategy.long)
if strategy.position_size > 0 and na(entryBar)
    entryBar := bar_index
exitNow = strategy.position_size > 0 and not na(entryBar) and bar_index - entryBar >= 20
if exitNow
    strategy.close("Long")
entryBar := exitNow ? na : entryBar
C# (QuantConnect)
using System;
using QuantConnect.Data;
using QuantConnect.Data.Market;
using QuantConnect.Indicators;

namespace QuantConnect.Algorithm.CSharp
{
    public class ThreeDownDays : QCAlgorithm
    {
        private Symbol _aapl;
        private Symbol _qqq;
        private ExponentialMovingAverage _ema50;
        private ExponentialMovingAverage _ema200;
        private RollingWindow<decimal> _closes = new RollingWindow<decimal>(4);
        private int _daysInTrade = -1;

        public override void Initialize()
        {
            SetStartDate(2019, 1, 1);
            SetEndDate(2025, 12, 31);
            SetCash(100000);

            _aapl = AddEquity("AAPL", Resolution.Daily).Symbol;
            _qqq = AddEquity("QQQ", Resolution.Daily).Symbol;

            _ema50 = EMA(_qqq, 50, Resolution.Daily);
            _ema200 = EMA(_qqq, 200, Resolution.Daily);
        }

        public override void OnData(Slice slice)
        {
            if (!slice.Bars.ContainsKey(_aapl)) return;
            _closes.Add(slice.Bars[_aapl].Close);

            if (Portfolio[_aapl].Invested)
            {
                _daysInTrade++;
                if (_daysInTrade >= 20)
                {
                    Liquidate(_aapl);
                    _daysInTrade = -1;
                }
                return;
            }

            if (!_closes.IsReady || !_ema50.IsReady || !_ema200.IsReady) return;

            bool marketOk = _ema50 > _ema200;
            bool threeDown = _closes[0] < _closes[1] && _closes[1] < _closes[2] && _closes[2] < _closes[3];

            if (marketOk && threeDown)
            {
                SetHoldings(_aapl, 1.0);
                _daysInTrade = 0;
            }
        }
    }
}

MACD crossover

A complete, ready-to-run version. Same paste steps as above. Nothing else to add or edit.

Pine Script (TradingView)
//@version=6
strategy("MACD Crossover", overlay=true, default_qty_type=strategy.percent_of_equity, default_qty_value=100)
startDate = timestamp("2019-01-01")
endDate = timestamp("2025-12-31")
inWindow = time >= startDate and time <= endDate
qqqClose = request.security("QQQ", "D", close)
qqqEma50 = ta.ema(qqqClose, 50)
qqqEma200 = ta.ema(qqqClose, 200)
marketOK = qqqEma50 > qqqEma200
[macdLine, signalLine, _] = ta.macd(close, 12, 26, 9)
signal = marketOK and ta.crossover(macdLine, signalLine) and inWindow
var int entryBar = na
if signal and strategy.position_size == 0
    strategy.entry("Long", strategy.long)
if strategy.position_size > 0 and na(entryBar)
    entryBar := bar_index
exitNow = strategy.position_size > 0 and not na(entryBar) and bar_index - entryBar >= 20
if exitNow
    strategy.close("Long")
entryBar := exitNow ? na : entryBar
C# (QuantConnect)
using System;
using QuantConnect.Data;
using QuantConnect.Data.Market;
using QuantConnect.Indicators;

namespace QuantConnect.Algorithm.CSharp
{
    public class MacdCrossover : QCAlgorithm
    {
        private Symbol _aapl;
        private Symbol _qqq;
        private ExponentialMovingAverage _ema50;
        private ExponentialMovingAverage _ema200;
        private MovingAverageConvergenceDivergence _macd;
        private bool _macdWasBelow;
        private int _daysInTrade = -1;

        public override void Initialize()
        {
            SetStartDate(2019, 1, 1);
            SetEndDate(2025, 12, 31);
            SetCash(100000);

            _aapl = AddEquity("AAPL", Resolution.Daily).Symbol;
            _macd = MACD(_aapl, 12, 26, 9, MovingAverageType.Exponential, Resolution.Daily);
            _qqq = AddEquity("QQQ", Resolution.Daily).Symbol;
            _ema50 = EMA(_qqq, 50, Resolution.Daily);
            _ema200 = EMA(_qqq, 200, Resolution.Daily);
        }

        public override void OnData(Slice slice)
        {
            if (!slice.Bars.ContainsKey(_aapl)) return;

            // Update indicator state on every bar, even while a trade is open, so a crossover is never missed.
            if (!_macd.IsReady) return;
            decimal macdLine = _macd.Current.Value;
            decimal signalLine = _macd.Signal.Current.Value;
            bool crossedUp = _macdWasBelow && macdLine > signalLine;
            _macdWasBelow = macdLine <= signalLine;

            if (Portfolio[_aapl].Invested)
            {
                _daysInTrade++;
                if (_daysInTrade >= 20)
                {
                    Liquidate(_aapl);
                    _daysInTrade = -1;
                }
                return;
            }

            if (!_ema50.IsReady || !_ema200.IsReady) return;
            bool marketOk = _ema50.Current.Value > _ema200.Current.Value;

            if (marketOk && crossedUp)
            {
                SetHoldings(_aapl, 1.0);
                _daysInTrade = 0;
            }
        }
    }
}

RSI oversold bounce

A complete, ready-to-run version. Same paste steps as above. Nothing else to add or edit.

Pine Script (TradingView)
//@version=6
strategy("RSI Oversold Bounce", overlay=true, default_qty_type=strategy.percent_of_equity, default_qty_value=100)
startDate = timestamp("2019-01-01")
endDate = timestamp("2025-12-31")
inWindow = time >= startDate and time <= endDate
qqqClose = request.security("QQQ", "D", close)
qqqEma50 = ta.ema(qqqClose, 50)
qqqEma200 = ta.ema(qqqClose, 200)
marketOK = qqqEma50 > qqqEma200
rsiValue = ta.rsi(close, 14)
signal = marketOK and ta.crossover(rsiValue, 30) and inWindow
var int entryBar = na
if signal and strategy.position_size == 0
    strategy.entry("Long", strategy.long)
if strategy.position_size > 0 and na(entryBar)
    entryBar := bar_index
exitNow = strategy.position_size > 0 and not na(entryBar) and bar_index - entryBar >= 20
if exitNow
    strategy.close("Long")
entryBar := exitNow ? na : entryBar
C# (QuantConnect)
using System;
using QuantConnect.Data;
using QuantConnect.Data.Market;
using QuantConnect.Indicators;

namespace QuantConnect.Algorithm.CSharp
{
    public class RsiOversoldBounce : QCAlgorithm
    {
        private Symbol _aapl;
        private Symbol _qqq;
        private ExponentialMovingAverage _ema50;
        private ExponentialMovingAverage _ema200;
        private RelativeStrengthIndex _rsi;
        private bool _wasOversold;
        private int _daysInTrade = -1;

        public override void Initialize()
        {
            SetStartDate(2019, 1, 1);
            SetEndDate(2025, 12, 31);
            SetCash(100000);

            _aapl = AddEquity("AAPL", Resolution.Daily).Symbol;
            _rsi = RSI(_aapl, 14, MovingAverageType.Wilders, Resolution.Daily);
            _qqq = AddEquity("QQQ", Resolution.Daily).Symbol;
            _ema50 = EMA(_qqq, 50, Resolution.Daily);
            _ema200 = EMA(_qqq, 200, Resolution.Daily);
        }

        public override void OnData(Slice slice)
        {
            if (!slice.Bars.ContainsKey(_aapl)) return;

            // Update indicator state on every bar, even while a trade is open, so a crossover is never missed.
            if (!_rsi.IsReady) return;
            decimal rsiValue = _rsi.Current.Value;
            bool crossedBackAbove30 = _wasOversold && rsiValue > 30m;
            _wasOversold = rsiValue < 30m;

            if (Portfolio[_aapl].Invested)
            {
                _daysInTrade++;
                if (_daysInTrade >= 20)
                {
                    Liquidate(_aapl);
                    _daysInTrade = -1;
                }
                return;
            }

            if (!_ema50.IsReady || !_ema200.IsReady) return;
            bool marketOk = _ema50.Current.Value > _ema200.Current.Value;

            if (marketOk && crossedBackAbove30)
            {
                SetHoldings(_aapl, 1.0);
                _daysInTrade = 0;
            }
        }
    }
}

Golden cross

A complete, ready-to-run version. Same paste steps as above. This one does not use the QQQ market check, matching the chapter.

Pine Script (TradingView)
//@version=6
strategy("Golden Cross", overlay=true, default_qty_type=strategy.percent_of_equity, default_qty_value=100)
startDate = timestamp("2019-01-01")
endDate = timestamp("2025-12-31")
inWindow = time >= startDate and time <= endDate
sma50 = ta.sma(close, 50)
sma200 = ta.sma(close, 200)
signal = ta.crossover(sma50, sma200) and inWindow
var int entryBar = na
if signal and strategy.position_size == 0
    strategy.entry("Long", strategy.long)
if strategy.position_size > 0 and na(entryBar)
    entryBar := bar_index
exitNow = strategy.position_size > 0 and not na(entryBar) and bar_index - entryBar >= 20
if exitNow
    strategy.close("Long")
entryBar := exitNow ? na : entryBar
C# (QuantConnect)
using System;
using QuantConnect.Data;
using QuantConnect.Data.Market;
using QuantConnect.Indicators;

namespace QuantConnect.Algorithm.CSharp
{
    public class GoldenCross : QCAlgorithm
    {
        private Symbol _aapl;
        private SimpleMovingAverage _sma50;
        private SimpleMovingAverage _sma200;
        private bool _wasBelow;
        private int _daysInTrade = -1;

        public override void Initialize()
        {
            SetStartDate(2019, 1, 1);
            SetEndDate(2025, 12, 31);
            SetCash(100000);

            _aapl = AddEquity("AAPL", Resolution.Daily).Symbol;
            _sma50 = SMA(_aapl, 50, Resolution.Daily);
            _sma200 = SMA(_aapl, 200, Resolution.Daily);
        }

        public override void OnData(Slice slice)
        {
            if (!slice.Bars.ContainsKey(_aapl)) return;

            // Update indicator state on every bar, even while a trade is open, so a crossover is never missed.
            if (!_sma50.IsReady || !_sma200.IsReady) return;
            bool crossedUp = _wasBelow && _sma50.Current.Value > _sma200.Current.Value;
            _wasBelow = _sma50.Current.Value <= _sma200.Current.Value;

            if (Portfolio[_aapl].Invested)
            {
                _daysInTrade++;
                if (_daysInTrade >= 20)
                {
                    Liquidate(_aapl);
                    _daysInTrade = -1;
                }
                return;
            }

            if (crossedUp)
            {
                SetHoldings(_aapl, 1.0);
                _daysInTrade = 0;
            }
        }
    }
}

Bollinger Band touch

A complete, ready-to-run version. Same paste steps as above. Nothing else to add or edit.

Pine Script (TradingView)
//@version=6
strategy("Bollinger Band Touch", overlay=true, default_qty_type=strategy.percent_of_equity, default_qty_value=100)
startDate = timestamp("2019-01-01")
endDate = timestamp("2025-12-31")
inWindow = time >= startDate and time <= endDate
qqqClose = request.security("QQQ", "D", close)
qqqEma50 = ta.ema(qqqClose, 50)
qqqEma200 = ta.ema(qqqClose, 200)
marketOK = qqqEma50 > qqqEma200
[_, _, lowerBand] = ta.bb(close, 20, 2)
signal = marketOK and close <= lowerBand and inWindow
var int entryBar = na
if signal and strategy.position_size == 0
    strategy.entry("Long", strategy.long)
if strategy.position_size > 0 and na(entryBar)
    entryBar := bar_index
exitNow = strategy.position_size > 0 and not na(entryBar) and bar_index - entryBar >= 20
if exitNow
    strategy.close("Long")
entryBar := exitNow ? na : entryBar
C# (QuantConnect)
using System;
using QuantConnect.Data;
using QuantConnect.Data.Market;
using QuantConnect.Indicators;

namespace QuantConnect.Algorithm.CSharp
{
    public class BollingerBandTouch : QCAlgorithm
    {
        private Symbol _aapl;
        private Symbol _qqq;
        private ExponentialMovingAverage _ema50;
        private ExponentialMovingAverage _ema200;
        private BollingerBands _bb;
        private int _daysInTrade = -1;

        public override void Initialize()
        {
            SetStartDate(2019, 1, 1);
            SetEndDate(2025, 12, 31);
            SetCash(100000);

            _aapl = AddEquity("AAPL", Resolution.Daily).Symbol;
            _bb = BB(_aapl, 20, 2m, MovingAverageType.Simple, Resolution.Daily);
            _qqq = AddEquity("QQQ", Resolution.Daily).Symbol;
            _ema50 = EMA(_qqq, 50, Resolution.Daily);
            _ema200 = EMA(_qqq, 200, Resolution.Daily);
        }

        public override void OnData(Slice slice)
        {
            if (!slice.Bars.ContainsKey(_aapl)) return;

            // Update indicator state on every bar, even while a trade is open, so a crossover is never missed.
            if (!_bb.IsReady) return;
            bool touchedLowerBand = slice.Bars[_aapl].Close <= _bb.LowerBand.Current.Value;

            if (Portfolio[_aapl].Invested)
            {
                _daysInTrade++;
                if (_daysInTrade >= 20)
                {
                    Liquidate(_aapl);
                    _daysInTrade = -1;
                }
                return;
            }

            if (!_ema50.IsReady || !_ema200.IsReady) return;
            bool marketOk = _ema50.Current.Value > _ema200.Current.Value;

            if (marketOk && touchedLowerBand)
            {
                SetHoldings(_aapl, 1.0);
                _daysInTrade = 0;
            }
        }
    }
}