How to Backtest Stock Chart Patterns: A Step-by-Step Guide Using Real Data
Why Most Pattern Backtests Are Misleading
Every trading education site tells you to "backtest your strategy." But traditional pattern backtesting has a fatal flaw: it relies on rigid geometric rules to detect patterns. A bull flag detector might require the pullback to be exactly 38-50% of the pole, lasting 5-10 bars, with declining volume. Real patterns are messier than that.
The result is either over-detection (flagging every minor pullback as a bull flag) or under-detection (missing obvious patterns because one parameter is slightly off). Either way, the backtest statistics are unreliable.
Chart Library takes a fundamentally different approach. Instead of defining patterns with geometric rules, we use 384-dimensional embedding vectors that capture the full shape of price action, volume dynamics, volatility, and VWAP deviation. Two charts are "similar" if their embedding vectors are close together in Euclidean space — no arbitrary parameter thresholds required.
What You Need
To follow along, you need a Chart Library API key (free tier gives you 200 calls/day) and either Python or any HTTP client. We will use the Python SDK for clarity.
- Sign up at chartlibrary.io/developers and copy your API key
- Install the SDK: pip install chartlibrary
- Set your environment variable: export CHART_LIBRARY_KEY=cl_your_key_here
Note:The free tier (200 calls/day) is enough to run pattern backtests across dozens of setups. For larger-scale research, the Builder tier ($29/mo) gives you 5,000 calls/day.
Step 1: Find Your Pattern
Start with a specific chart you want to test. Maybe you spotted a bull flag on AAPL today and want to know how similar setups have played out historically. The /search endpoint finds the 10 most similar historical chart patterns.
import chartlibrary as cl client = cl.Client() results = client.search("AAPL", date="2026-04-01") for match in results["matches"]: print(f"{match['symbol']} {match['date']} " f"similarity={match['similarity']:.1f}%")
This returns 10 historical charts whose embedding vectors are closest to AAPL on April 1, 2026. Each match includes the ticker, date, similarity score, and forward return data.
Step 2: Analyze Forward Returns
The real power of pattern backtesting is seeing what happened AFTER each matching pattern. Chart Library pre-computes forward returns at 1, 3, 5, and 10-day horizons for every match.
stats = results["forward_returns"] for horizon in ["1d", "5d", "10d"]: data = stats[horizon] print(f"{horizon}: avg={data['mean']:.2f}% " f"win_rate={data['win_rate']:.0f}% " f"n={data['count']}")
This gives you a statistical profile of what happened after patterns that looked like your current chart. An average 5-day return of +1.5% with a 60% win rate across 10 matches is a meaningful signal. A 50% win rate with 0% average return tells you the pattern is noise.
Step 3: Test Across Multiple Instances
One search tests one pattern. To properly backtest, you want to find multiple instances of a pattern type and measure aggregate performance. For example, to backtest bull flags across 50 different stocks:
import json tickers = ["AAPL", "NVDA", "TSLA", "MSFT", "AMD", "META", "AMZN", "GOOG", "CRM", "NFLX"] all_returns = [] for ticker in tickers: result = client.search(ticker) if result and result.get("patterns"): for p in result["patterns"]: if p["type"] == "bull_flag" and p["confidence"] > 0.6: all_returns.append(result["forward_returns"]) print(f"Found {len(all_returns)} bull flag instances")
By aggregating forward returns across dozens of bull flag instances, you get statistically meaningful backtest results instead of anecdotes from a single chart.
Step 4: Compare to Base Rates
This is the step most traders skip, and it is the most important. A 55% win rate sounds impressive until you learn that the base rate for "stock goes up over 5 days" is already 52%. You need to compare your pattern's performance against what a random entry would produce.
Chart Library's forward returns cache contains 23.6 million rows of pre-computed returns across all stocks and dates. The aggregate base rates from this data are approximately:
- 1-day base rate: +0.04% average return, 51.2% win rate
- 5-day base rate: +0.21% average return, 52.4% win rate
- 10-day base rate: +0.43% average return, 53.1% win rate
Tip:Any pattern with a 5-day win rate below 53% is not reliably outperforming a random entry. Focus on patterns that show at least a 3-5 percentage point edge over the base rate.
Step 5: Use the Trade Simulator
Raw forward returns don't account for how you would actually trade the pattern. Chart Library's Trade Simulator lets you apply stop losses, profit targets, and hold periods to see realistic P&L across all 10 historical matches.
sim = client.simulate( symbol="AAPL", date="2026-04-01", stop_loss=-2.0, # -2% stop profit_target=4.0, # +4% target hold_days=5 # max 5 day hold ) print(f"Win rate: {sim['win_rate']}%") print(f"Avg P&L: {sim['avg_pnl']:.2f}%") print(f"Max drawdown: {sim['max_drawdown']:.2f}%")
This tells you whether the pattern is tradeable with realistic risk management, not just whether it tends to go up or down.
What We Learned From 16,000+ Forward Tests
Chart Library runs automated forward tests every trading day. After 16,000+ predictions with tracked actuals, here are the aggregate findings:
- Patterns with 90%+ similarity scores have meaningfully higher direction accuracy than weaker matches
- 5-day horizon shows the strongest signal — 1-day is too noisy, 10-day is diluted by intervening events
- The mean predicted return across 10 matches is a better predictor than any individual match
- High fan chart dispersion (matches disagree on direction) reliably predicts elevated future volatility
- Volume-confirmed patterns outperform by roughly 3 percentage points in win rate
Start Your Backtest
Pattern backtesting should not be about confirming your bias. It should answer the question: "Has this exact setup produced a statistical edge in the past?" Chart Library gives you the tools to answer that question with real data across 24 million historical patterns.
Get a free API key at chartlibrary.io/developers and start backtesting patterns against 10 years of historical data.
Ready to try Chart Library?
Upload a chart screenshot or search any ticker — see what history says about your pattern.
Try it freeRelated Articles
Bull Flag Success Rate: Updated Data From 24 Million Chart Patterns
Updated 2026 analysis of bull flag success rates across 24 million chart patterns. Real win rates by timeframe, what separates winners from losers, and how bull flags compare to random entries.
How AI Agents Analyze Stocks: A Complete Guide to Chart Library's MCP Tools
Learn how AI agents like Claude and ChatGPT use MCP tools to analyze stock charts. Set up Chart Library's MCP server in 5 minutes and give your AI access to 24 million historical chart patterns.
Pattern Similarity Search vs Traditional Technical Indicators: What's the Difference?
How does vector similarity search compare to rule-based technical indicators like RSI, MACD, and named patterns? We break down the approaches, trade-offs, and when to use each.