Trade Like a Machine
A step-by-step guide to building a rules-based options trading system. No hunches and no guru worship, just rules you can write down, test, and follow.
Get it on Amazon See what's inside
Why I'm writing it
This is my third book, and it's the one many people have asked me for. For years I've been asked to share what I've learned building a full-blown automated trading system. This is my attempt.
Over the years I've worked with more than six thousand options traders, and the same three problems kept showing up. Most people have a strategy but not a system. Most understand their rules but drop them the moment a trade goes bad. And most spend far more energy trying to win more than trying to lose less.
The book uses real charts and real data instead of hunches, and it's honest about the ideas that didn't work, because those are often the most useful ones.
What's inside
Twenty-two chapters in four parts, plus five appendices, building from the mistake most traders make to a complete, testable system.
Part One. Why You Need a System
Why systems beat gut feel, and why most traders lose even when they are right.
- 1The Hardest Thing in Trading Isn't Finding a TradeWhy being right about a trade still isn't enough without a system to protect you from yourself.
- 2Why Most Traders Lose (And It Has Nothing to Do With Bad Luck)Why smart, educated traders still lose, and why intelligence was never the missing piece.
- 3From Gut Feelings to a Rule Book: My StoryThe author's own story, from chasing tips and hunches to writing down real rules.
- 4The Difference Between a Strategy and a SystemWhat separates a strategy from a complete, testable trading system.
Part Two. Building the System
The pieces of a complete system, one at a time: what you trade, indicators, reading the market, entries, the trade itself, exits and sizing.
- 5No Universe, No SystemHow to build and maintain a fixed watchlist of liquid stocks before you trade a single one.
- 6Indicators as Building Blocks (Not Magic Wands)How to use EMA, MACD, RSI, and other indicators correctly instead of chasing a new one every time one stops working.
- 7Reading the Market Before You Read a StockWhy checking the overall market comes before checking any stock, including how Maya's own launch handled a crash in its first weeks.
- 8Entries: Making Every Stock Earn Its Way InThe entry checklist every stock has to pass, gate by gate, before a trade opens.
- 9Five Decisions, One TradeHow to build the actual options position: strike, expiration, price, and size, decided in advance.
- 10Winners and Losers Look Identical at the DoorFour real trades, walked through step by step, including one that did everything right and still lost.
- 11Exits: Deciding Before You EnterWhy exits have to be decided before a trade opens, and what the backtest says each one is actually worth.
- 12Sizing Without Blowing UpHow much to risk per trade, why correlated losses cluster together, and where the Kelly criterion breaks down.
Part Three. Proving It
How to test your own rules in a spreadsheet, and what the real results show.
- 13Backtesting Without a PhDHow to build a real backtest in a spreadsheet, no coding required.
- 14What the Backtests Actually ShowAn honest read of seven years and 2,361 trades, including the losing year and the losing streaks.
Part Four. Running It
Going live, why a signal is not a system, getting back in after a bad stretch, judging results like a professional, stress-testing against history it never saw, a hands-on workbook, running the rules without code, and staying consistent.
- 15Before You Go Live: A Pre-Launch ChecklistSharpe ratio, drawdown, and the honest question of whether you can survive the ride before you trade it live.
- 16A Signal Is Not a SystemWhy a chart alert is a good front door but not a finished trading system on its own.
- 17After the Crash, the Harder TradeWhen it's actually safe to start trading again after a crash, using Maya's real April 2025 restart as the example.
- 18What the Return Number Doesn't Tell YouProfit factor and expectancy: why a headline win rate can hide a losing system.
- 19Ten Thousand Alternate TimelinesSimulating thousands of alternate market histories to see how much of a good result was just luck.
- 20The WorkbookA hands-on workbook to build and test your own version of the system.
- 21You Don't Need to Code to Run ThisRunning the whole system by hand with a brokerage account, a spreadsheet, and a notebook.
- 22The Real Edge: Consistency Over TimeWhat it actually costs to follow the rules on the week they're hardest to follow.
Appendices
The system on one page, what AI can do for a trader now, frequently asked questions, resources and a glossary.
- AThe Complete System at a GlanceThe entire system condensed to a few pages, in the order a trading day runs.
- BWhat AI Can Do for a Trader NowWhat today's AI tools can and can't do for a trader, broker by broker.
- CFrequently Asked QuestionsStraight answers to the questions readers ask most.
- DResources and Further ReadingA short list of real books and tools worth your time.
- EGlossary of TermsEvery term used in the book, defined in plain language.
Real charts, real data
Every chart in the book is drawn from real market data, including the ones that don't flatter the idea. Here are two of them.
Full-color images from the book
The printed book is black and white. Here is every chart, diagram and screenshot in full color and full size, grouped by chapter. Click any image to open it, or download it. Reading on a Kindle app, a Fire tablet or read.amazon.com? Those show the images in color too, though an e-ink Kindle shows gray. Looking for the code? Jump to the code samples.
Chapter 1: The Hardest Thing in Trading Isn't Finding a TradeBack to top




Chapter 3: From Gut Feelings to a Rule Book: My StoryBack to top


Chapter 4: The Difference Between a Strategy and a SystemBack to top



Chapter 5: No Universe, No SystemBack to top


Chapter 6: Indicators as Building Blocks (Not Magic Wands)Back to top










Chapter 7: Reading the Market Before You Read a StockBack to top








Chapter 8: Entries: Making Every Stock Earn Its Way InBack to top




Chapter 9: Five Decisions, One TradeBack to top





Chapter 10: Winners and Losers Look Identical at the DoorBack to top




Chapter 11: Exits: Deciding Before You EnterBack to top





Chapter 12: Sizing Without Blowing UpBack to top




Chapter 13: Backtesting Without a PhDBack to top

















Chapter 14: What the Backtests Actually ShowBack to top




Chapter 16: A Signal Is Not a SystemBack to top




Chapter 17: You Don't Need to Code to Run ThisBack to top


Chapter 18: The Real Edge: Consistency Over TimeBack to top



Chapter 19: After the Crash, the Harder TradeBack to top

Chapter 20: What the Return Number Doesn't Tell YouBack to top

Chapter 21: Ten Thousand Alternate TimelinesBack to top

Chapter 22: The WorkbookBack to top










See it running live
These images come from the research behind Maya, the automated trading system at TradeWithMaya.com. Members see its trades as they happen, every entry and every exit.
Code from the book
Every code example from the backtesting chapter, in full. 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.
//@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 : entryBarusing 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.
//@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 : entryBarusing 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.
//@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 : entryBarusing 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.
//@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 : entryBarusing 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.
//@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 : entryBarusing 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;
}
}
}
}Prefer files? Download everything as a zip: every strategy as a complete, ready-to-run TradingView (.pine) and QuantConnect (.cs) file.
About the author
Nishant Pant has traded for 20 years, the last 15 of them in options, and has taught more than six thousand traders. This is his third book, after the $25K Options Trading Challenge (2019) and Mean Reversion Trading (2022). He built Maya, a fully automated trading system, to put these rules to work, and he wrote this book so you can build your own.
Educational only, not investment advice. Options involve substantial risk, including the loss of the entire amount invested. Charts and results shown are examples drawn from historical data. Backtested results are hypothetical, do not represent actual trading, and past performance does not guarantee future results.