How to Backtest a Trading Strategy: The +897.1% MFE That Was Never Tradable

By SNACS Trade · 2026-10-27T12:45:00.843078+00:00

RBNE showed a +897.1% range that never existed as a trade. Here's how to backtest small-cap strategies with point-in-time, session-aware data.

TLDR

How Do You Backtest a Trading Strategy?

To backtest a trading strategy, write your rules precisely enough that someone else could run them without asking you a question. Then replay those rules on historical data, using only what you could have known at each decision point, and measure the result after realistic fills. That's the whole job. In small caps, the hard part is the middle step: only what you could have known.

Large-cap backtests can get away with daily bars because the action happens between 9:30 a.m. and 4:00 p.m. Eastern, on tight spreads, with stable share counts. Small caps break every one of those assumptions. The move often starts at 4:00 a.m. in pre-market. The spread on a sub-$1 stock can eat a real share of a 20% move. A 424B5 overnight changes the float, and a reverse split rewrites every historical price. And the stock that ran yesterday shows up on every gainers list today, while the names that tried and failed are gone from view.

Every backtest has to answer three questions honestly:

  1. What did I know, and when did I know it? If you enter at 9:31 a.m., your filter can only use data stamped 9:31 a.m. or earlier. Full-day volume, full-day RVOL, the day's high and low, and any press release that crossed later are off-limits.
  2. Could I actually have filled? A backtest that buys the exact low and sells the exact high measures the stock, not the strategy. Model the spread and the slippage on the trigger. Cap your size as a fraction of the volume traded at the trigger, and account for halts.
  3. Is my sample the full population or the highlight reel? Test every ticker that met your filter on the test date, including the ones that faded, got halted, reverse split or delisted.

The metrics that matter aren't complicated. Track expectancy (average dollar result per trade across winners and losers), profit factor (gross wins divided by gross losses), maximum drawdown (the deepest run of consecutive losses), and MFE capture (how much of the available move your exit rule kept). Retail backtests use MFE capture less than any other number. If a rule enters well but exits with a small slice of the available move, you have an exit problem, not an entry problem. You only see that when you log the MFE next to the result.

The mirror metric is maximum adverse excursion: how far price moved against you before the trade resolved. It tells you where your stop belongs. If your winners rarely pulled back more than a few percent below entry, a wider stop pays for losers without protecting winners.

One more point from experience: the backtest isn't the finish line. It's a filter that tells you which ideas deserve a small-size live trial. The live trial, logged in a journal, is the real backtest, because it includes you, your hesitation and your broker's fills.

image-4

What Are the Six Steps of a Small-Cap Backtest?

A small-cap backtest has six steps: hypothesis, mechanical rules, point-in-time universe, session-aware data, realistic fills, and out-of-sample validation. Each step has its own small-cap trap.

Step What You Do The Small-Cap Trap
1. Hypothesis State one testable idea: 'stocks with a same-day filing or press release that break their pre-market high continue into the close' Vague ideas ('buy strength') can't be proven wrong
2. Mechanical rules Define entry trigger, stop, exit, position size, time window Discretionary words like 'clean' or 'strong' sneak in
3. Point-in-time universe Include every ticker that met the filter at the trigger timestamp Testing on a gainers list built after the fact
4. Session-aware data Use pre-market, regular and after-hours prices separately; handle splits and dividends Daily bars hide where the high and low printed
5. Realistic fills Model spread, slippage, halts, and size as a share of volume Assuming fills at the exact low or high
6. Out-of-sample + live Test on a period you didn't build on, then trade small and journal Curve-fitting until the past looks perfect

Most traders skip step three. If you pull a top-gainers list and test rules on it, your results will be inflated, because the list was built with hindsight. The correct universe is every ticker that met your filter at the moment your rule would have fired.

Worked Example #1: Backtesting Relative-Volume Spikes

The most common small-cap hypothesis is that extreme relative volume predicts continuation. Testing it on five of the biggest relative-volume sessions from Oct 20 through Oct 26 shows why the idea has teeth, and how easily a backtest of it can lie.

Version 1 rule: on any stock trading at 500x or more of its 50-day average volume, buy the regular-session open and sell the regular-session close. Five sessions in the data qualify:

image-1

Ticker Date Volume RVOL Pre-Mkt High Open Close Close vs Prior True MFE
RBNE Oct 21 150.5M 1826.8x $2.10 $1.00 $0.23 -76.8% +897.1%
POM Oct 21 48.8M 1377.8x $3.55 $0.95 $3.01 +216.8% +753.6%
AZTR Oct 26 148.4M 1063.0x $1.44 $1.04 $1.53 +47.1% +166.2%
PRSO Oct 20 230.7M 950.6x $0.46 $0.42 $0.59 +41.0% +67.6%
AIOK Oct 23 42.8M 654.9x $4.68 $5.55 $12.02 +116.6% +282.1%

On the surface, four of the five closed the regular session above their open. RBNE was the exception: it opened at $1.00 and closed at $0.23. AIOK opened at $5.55 and closed at $12.02. POM opened at $0.95 and closed at $3.01.

That looks like a strategy. It isn't one yet, because the test has two lookahead errors in it.

The RVOL column wasn't available at 9:30. RBNE's 1826.8x is an end-of-day number. At the open, you only had pre-market cumulative volume. Worse, these five sessions are in the table because they finished as the top spikes. Every session that spiked early and died at a modest RVOL is missing, and those are exactly the trades a live version of this rule would have taken. The fix is to compute RVOL as of your entry timestamp and pull every ticker that crossed the threshold at that time, not at the close.

The MFE column isn't a trade. Using a $10,000 base, RBNE's +897.1% reads as $89,710 (+897.1%) of profit on paper. Pitfall #1 below shows why that number never existed.

Version 2 rule: use only data you have at the open. Enter when the regular session trades above the pre-market high, and exit at the regular-session close. The pre-market high is fully known by 9:30.

Ticker Pre-Mkt High Open Regular-Session High Triggered? Close
PRSO $0.46 $0.42 $0.65 Yes $0.59
AZTR $1.44 $1.04 $2.13 Yes $1.53
AIOK $4.68 $5.55 $15.59 Yes (opened above) $12.02
POM $3.55 $0.95 $3.01 No $3.01
RBNE $2.10 $1.00 $1.01 No $0.23

Three sessions triggered, and all three closed above their pre-market high. PRSO cleared $0.46 and closed at $0.59. AZTR cleared $1.44 and closed at $1.53. AIOK opened at $5.55, already above its $4.68 pre-market high, and closed at $12.02.

The two skipped sessions show both sides of the filter. RBNE's regular session topped at $1.01, nowhere near its $2.10 pre-market high, so the rule kept you out of the $0.23 close. POM's regular-session high of $3.01 never reached its $3.55 pre-market high, so the rule also skipped a session that closed +216.8%. Every filter has a cost like that. The backtest's job is to show you both sides of it before you trade it.

This daily session data can't tell you whether a stop below the trigger got hit before the close. AZTR traded as low as $0.91 in the regular session. Whether that low came before or after the $1.44 break takes intraday bars to answer. Five sessions also aren't a sample. They're a worked example. The pre-market high as an ignition level is covered in depth in our momentum trading framework for small caps.

Worked Example #2: AIOK and CLRO, Testing the Exit Instead of the Entry

Most backtests obsess over entries, but the exit rule decides the result. AIOK and CLRO had similar five-day gains, and the best exit rule for each was different.

AIOK on Oct 23 (last week). AIOK filed two 6-K filings on Oct 23, alongside press releases announcing a special dividend of $3.50 per share and the sale of the carrier Dream Arrax. Pre-market traded $4.08 to $4.68. The regular session opened at $5.55, ran to $15.59, dipped as low as $5.40 and closed at $12.02. After-hours closed at $9.82. Volume was 42.8M shares at 654.9x average. A position capturing the full +282.1% MFE would have returned $28,210 (+282.1%).

image-0

Now run three exit rules on the same entry:

Same entry, three different results. For AIOK, the regular-session close was the best of the three. The volume explains why: 42,814,664 of the 45,962,607 shares AIOK traded over those five sessions came in that single Oct 23 session. That was a one-day event, not a multi-day trend.

The special dividend is also a backtest trap. Adjusted price series rewrite every price before the ex-date to account for the payout. A $5.55 entry on the raw tape won't match the same bar in an adjusted series. Pick one series for both rules and results, and don't mix them.

CLRO from Oct 20 to Oct 26. CLRO ran +164.4% over five sessions, from $1.88 to $4.97, with 120,140,125 shares traded in total and a peak session of 83,109,004. The specific catalyst wasn't identified in available press releases. Here the hold rule was the trade: holding through the window would have returned $16,440 (+164.4%). Communications Equipment, CLRO's sector, saw average RVOL rotate in from 0.85 to 1.39 (+64%) week over week.

image-2

If one exit rule had been applied to both names, one of them would have been managed wrong. That's why you segment exit rules by setup type: single-session catalyst spikes in one bucket, multi-day continuation in another. Then test each bucket on its own.

The Oct 20-26 multi-day list also shows why losers have to be in the test set:

Ticker Start End 5-Day Change Peak Session Volume
CLRO $1.88 $4.97 +164.4% 83,109,004
POM $0.43 $1.13 +161.0% 48,813,380
AIOK $4.01 $9.16 +128.4% 42,814,664
SAGT $0.41 $0.73 +77.6% 20,455,244
REED $0.38 $0.66 +73.5% 47,020,796
AHT $1.84 $3.05 +65.8% 29,799,436
BOLT $4.05 $6.59 +62.7% 12,876,532
AARD $3.80 $1.08 -71.6% 16,286,780
XXII $0.26 $0.09 -66.4% 219,587,060

XXII traded 219,587,060 shares in its peak session, more than any name in the table, and finished down -66.4%. A high-volume filter would have caught it, and a backtest built from a gainers list would never have seen it.

What Are the Most Common Backtesting Pitfalls in Small Caps?

Six pitfalls break most small-cap backtests: ignoring the time order of the high and low, using end-of-day filters for intraday entries, survivorship and hot-sample bias, unadjusted corporate actions, fantasy fills, and dilution data that isn't point-in-time. Each one is visible in recent data.

1. Treating Low-to-High as a Trade

Day-range MFE ignores which came first, the high or the low. On Oct 21, RBNE's pre-market traded $0.48 to $2.10. The regular session opened at $1.00, topped at $1.01, fell to $0.22 and closed at $0.23. After-hours closed at $0.22. The full-day range was $0.21 to $2.10, the source of the +897.1% MFE, but the high printed in pre-market and the lows came afterward. Nobody could buy $0.21 and sell $2.10 in that order.

image-3

POM shows the same problem from the other side. Its $5.89 day high on Oct 21 sits above both its $3.55 pre-market high and its $3.01 regular-session high, which puts it in after-hours, after the $3.01 close. After-hours then closed at $2.90. POM's +753.6% MFE equals $75,360 (+753.6%) on paper. A rule that exits at the regular-session close never touches $5.89. The filing trail behind that move is broken down in our POM forensic filing analysis.

Catalyst timing has the same ordering problem. RBNE's vessel acquisition press release and 6-K filing are dated Oct 23, two days after the Oct 21 spike. If an event study tags the Oct 21 move to that headline, it has the cause running backward in time.

2. End-of-Day Filters on Intraday Entries

If your entry happens at 9:45 a.m., any filter built from the full day's volume, RVOL, range or close is lookahead. This is the most common contamination in homemade spreadsheets, because daily data makes it invisible. Rebuild each filter using cumulative values as of the trigger time.

3. Survivorship and Hot-Sample Bias

A backtest built during a hot stretch learns the wrong lesson. Last week (Oct 19-23 plus the Oct 26 session in the window) produced 25 runners of 50% or more, against a 4-week baseline of ~8.0 per week. If you build a rule on that sample and size it as normal, you're overconfident in it. The same-weekday history shows the swing: the top Tuesday mover was SDEV +99% on Sep 29 and OLB +111% on Oct 6. On Oct 13 it was WFF -86%, and on Oct 20 it was CJMB -51%. Test across slow, steady and runner-heavy weeks, and tag every trade with the tape it came from.

4. Splits and Dividends

Reverse splits manufacture fake gains in raw data. GCTK announced a reverse stock split in its Oct 22 8-K filing and press release. In the Oct 26 session, it traded pre-market at $0.81 to $1.09, opened at $0.84, ran to $1.76 and closed at $1.61, closing +91.7% on 34.5M shares with a +129.1% MFE. If you mix pre-split and post-split prices without adjusting, the backtest books moves that never happened. The multi-day runner figures in this article are split-adjusted close-to-close for that reason.

5. Fantasy Fills

Even heavy volume doesn't guarantee your fill. PRSO opened at $0.42 on Oct 20 and traded 230.7M shares. On a sub-$1 stock, a few cents of spread and slippage on the trigger is a large share of the move. Model a fixed slippage on every entry and exit, cap position size at a small fraction of the volume traded up to the trigger, and treat halts as forced holds. Our guide to the best brokers for day trading small caps covers how session access and fills differ by broker.

6. Dilution Data That Isn't Point-in-Time

A 'no active shelf' filter has to read the shelf as of the trade date, not today. Across the small-cap universe there are ~6,200 active warrant facilities, ~3,300 active shelves, ~2,200 active ATM programs and ~1,600 active convertible notes. Those numbers change every day. In the latest 3-day window, MYSZ, ABOS and UUU filed S-3 shelf registrations, and 3 424B5 filings landed from 2 tickers (AMSS and ASST). For your filter, each of those facilities only exists from its filing date forward. ADUR, for example, completed underwritten raises on 2025-6-10 at $8.44, 2025-12-19 at $11.50 and 2026-6-10 at $15.20. A backtest of ADUR trades from early 2025 can't know about the later facilities.

How Could You Have Caught These Before They Ran?

In each case, the pre-signal was a same-day filing or press release plus pre-market activity. All of it was time-stamped before the regular-session move, which makes it exactly the kind of data you can use in a backtest entry condition.

For backtesting, the takeaway is simple: encode the timestamped fields as entry conditions. Filing date, press release time, pre-market high, pre-market volume and cash runway tier all exist before the bell. The close and the day's range don't.

How to Play This

Start with one setup in one session and build out from there. A focused, narrow test teaches you more than a broad one.

How to Find and Test These Setups in SNACS

You can run the full loop in SNACS: the scanner surfaces candidates in real time, the Playbook forward-tests your rules, the journal measures your execution, and the Data API feeds point-in-time history to your own backtest scripts.

Scanner, for building the point-in-time universe. In the SNACS scanner, filter on price, RVOL, float and the SEC filing type column, then sort by Velocity (5s/1m/5m) to see what's accelerating right now instead of what accelerated earlier. Save the filter set as a named Saved Scan, for example 'Pre-Mkt High Break + Filing', and link it to a Dynamic Watchlist so matching tickers fill in automatically during the session. That gives you a record of what met your filter at the time, which is the universe a forward test needs. Click any ticker to open the ticker details page, where you'll see the chart, the dilution risk panel (active shelf, ATM and warrant facilities), recent news and SEC filings in one place. News Flash turns a ticker blue when a headline crosses, so the catalyst timestamp is visible.

Dilution, two paths. The scanner's Dilution Alerts column flags active facilities in the stream. For depth, the Dilution Snapshot in SEC research shows active facility counts, shares at risk and the lowest exercise price, and the Filing Browser lets you check exactly when a shelf or 424B5 landed.

Playbook, for forward testing. Encode your rules in the AI Playbook Builder as steps (historical context → setup → trigger → entry → exit), each with its own timeframe. Live matching watches every scanner ticker, and a star appears when your pattern triggers. That gives you a forward test with no lookahead, because it can only see the present.

Journal, for the final backtest. The trading journal syncs fills from your broker and tags each trade by setup. AI Insights reports your MFE capture rate, your best and worst setups, and the times of day where you give back money. That last report is the one that tells you whether the backtest survived contact with you. Our frameworks on how to journal your trades cover which fields to log.

Data API, for your own backtest scripts. If you build backtests in code or with an AI assistant, the Data API serves the same data the scanner runs on: 22 years of daily history, pre-market and after-hours session data, and SEC-sourced dilution facts tied to the filing each number came from. Point-in-time queries ('cash, float and active facilities on 2024-12-31') close the dilution lookahead hole described above. The included MCP server connects Claude, Cursor, VS Code Copilot or Windsurf in one command. It provides data with provenance, not signals. For more on wiring an AI to real data, see AI tools for stock trading.

What to Watch Next

The next useful test is a forward one. Run your rule live at small size, log every trigger the rule produced (including the ones you passed on), and compare the journal against the backtest after enough sessions.

Tag every new trade with its backdrop. The macro call is Risk-Off / Consolidation, and the S&P 500 (SPY) sits at $765.37, -2.1% from its 52-week high. Sector rotation is another field to log. Average Energy RVOL moved from 1.26 to 7.42 (+487%) week over week, and Electronic Components moved from 1.07 to 2.77 (+159%). Keep logging filing dates as they land too. The latest 3-day window brought S-1 filings from IMNN, TRAW, TRAXV and GRML, 160 8-K filings from 150 tickers, and an 11-filing Form 4 cluster at NATH. Each of these is a timestamped field a future backtest can use honestly. If you're newer to the session structure, start with our session-by-session guide to the best time to trade.

A backtest that has never been wrong has never been tested. Leave the losers in, keep the timestamps honest, and let the journal have the last word.

FAQ

How do you backtest a trading strategy?

Write mechanical entry, stop, exit and sizing rules. Replay them on historical data using only information available at each decision time, then measure the results after realistic spreads and slippage. For small caps, use session-aware data (pre-market, regular and after-hours prices kept separate), include every ticker that met your filter (not just the gainers), and validate on a period you didn't use to build the rules.

Can I backtest a penny stock strategy with daily data only?

Only for multi-day hold strategies. Daily bars can't tell you whether the high or the low came first. On Oct 21, RBNE's day range implied a +897.1% MFE, but its $2.10 high printed in pre-market and its regular-session low of $0.22 came later. Intraday entries need intraday, session-aware data.

What is lookahead bias in backtesting?

Lookahead bias is using information in a backtest that wasn't available at the moment the trade decision was made. Common small-cap examples are filtering on full-day RVOL for a morning entry, using day-range MFE as a trade, tagging a move to a press release dated after it, and checking today's dilution facilities for a past trade date.

How many trades do I need for a valid backtest?

You need enough trades to cover different market conditions, not a single hot week. Last week's tape produced 25 runners of 50% or more, against a 4-week baseline of ~8.0. A rule tested only on weeks like that will overstate its edge. Spread your sample across slow, steady and runner-heavy weeks, and track results separately by backdrop.

Should I use split-adjusted data for small-cap backtests?

Yes. Use split-adjusted data and apply it consistently, because reverse splits are common in small caps and raw prices across a split create fake moves. GCTK announced a reverse split in an Oct 22 8-K filing before its +91.7% session on Oct 26. Handle special dividends the same way, such as AIOK's $3.50 per share.

What is the difference between backtesting and forward testing?

Backtesting replays rules on past data. Forward testing runs the same rules live, on data as it arrives, so lookahead is impossible. In SNACS, Playbook live matching marks tickers with a star when your rules trigger in real time, and the trading journal's AI Insights measures how your actual fills and MFE capture compare to the plan.

Are penny stocks a good investment?

Most penny stocks are trading vehicles, not long-term investments, because many fund themselves through share issuance. Across the small-cap universe there are ~2,200 active ATM programs and ~3,300 active shelves. The opportunity is in defined, backtested intraday and multi-day setups with strict risk control, not buy-and-hold.

Is a free end-of-day stock screener enough for penny stock strategy research?

A free end-of-day screener is fine for generating ideas, but it isn't enough for backtesting intraday strategies. It shows yesterday's closing values, which is the same end-of-day lookahead problem described above. Testing small-cap day trades needs time-stamped pre-market data, intraday volume and point-in-time filing data.

Start Free Trial

5 trading day free trial on Edge. Cancel anytime before your trial ends to avoid being charged.