<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Matthieu David</title>
    <description>The latest articles on DEV Community by Matthieu David (@matthieu_david_9bfe2b8e4f).</description>
    <link>https://dev.to/matthieu_david_9bfe2b8e4f</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3857791%2F839629de-623d-41d1-83f2-79a3319ef087.png</url>
      <title>DEV Community: Matthieu David</title>
      <link>https://dev.to/matthieu_david_9bfe2b8e4f</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/matthieu_david_9bfe2b8e4f"/>
    <language>en</language>
    <item>
      <title>Your backtest is lying to you: 6 ways future data leaks in</title>
      <dc:creator>Matthieu David</dc:creator>
      <pubDate>Fri, 22 May 2026 14:51:30 +0000</pubDate>
      <link>https://dev.to/matthieu_david_9bfe2b8e4f/your-backtest-is-lying-to-you-6-ways-future-data-leaks-in-5d9m</link>
      <guid>https://dev.to/matthieu_david_9bfe2b8e4f/your-backtest-is-lying-to-you-6-ways-future-data-leaks-in-5d9m</guid>
      <description>&lt;p&gt;If you have ever built a strategy with a gorgeous equity curve that fell apart the moment you traded it live, you probably did not have a bad strategy. You had a leak.&lt;/p&gt;

&lt;p&gt;Look-ahead bias is the bug that makes a backtest lie. Future information sneaks into a decision the strategy could not have known at the time, the historical results look incredible, and live trading is where reality collects the bill. ML people call it data leakage. Traders call it repainting. Same disease.&lt;/p&gt;

&lt;p&gt;I build a backtesting engine, so I have stepped on every one of these. Here are the six ways future data leaks in, from the obvious to the ones that quietly destroy you.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Signaling on the bar that has not closed yet
&lt;/h2&gt;

&lt;p&gt;The classic. You evaluate a condition on &lt;code&gt;close[0]&lt;/code&gt;, the current bar, and act on it. But in real time that bar is not finished. Its close is still moving. You are effectively deciding with information from the future of that candle.&lt;/p&gt;

&lt;p&gt;The fix is boring and absolute: evaluate on &lt;code&gt;close[1]&lt;/code&gt;, the last confirmed bar. Every signal, every indicator input. If your backtest uses the forming bar anywhere, the numbers are fiction.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Filling inside the bar you used to decide
&lt;/h2&gt;

&lt;p&gt;Subtler. Say your rule is "enter when price breaks the high of the previous range." You detect the break using a bar's high, then you fill the entry at a price inside that same bar. But you only knew the high existed because the bar already printed it. In live trading you would have been filled on the way up, at a worse price, or not at all.&lt;/p&gt;

&lt;p&gt;A candle gives you O, H, L, C and nothing about the path between them. If your entry and your detection both depend on the same candle's extremes, you are reading the future. Decide on the closed bar, fill on the next bar's open, and the lie disappears.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Repainting indicators
&lt;/h2&gt;

&lt;p&gt;Some indicators revise their own history. A value that showed X two bars ago now shows Y because new data arrived. ZigZag, certain pivot detectors, anything that anchors to a later confirmed point, and a lot of the flashier "smart money" tools.&lt;/p&gt;

&lt;p&gt;If you backtest against the repainted (final) values, you are testing on data that did not exist at decision time. The indicator looks psychic because it is. Test only against the value the indicator would have shown live, on the bar it would have shown it.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Normalizing with statistics from the whole series
&lt;/h2&gt;

&lt;p&gt;This is the one that catches the ML crowd. You z-score your feature: subtract the mean, divide by the standard deviation. Easy. Except you computed the mean and standard deviation over the entire dataset, including data that comes after each point.&lt;/p&gt;

&lt;p&gt;Every normalized value before today now carries a whisper of the future. Your model trains on it and looks brilliant in backtest. The fix is rolling, point-in-time statistics: at each bar, use only what was known up to that bar. Slower, correct.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Optimizing on the same data you report
&lt;/h2&gt;

&lt;p&gt;You try 500 parameter combinations on 2016 to 2026, pick the best Sharpe, and report that Sharpe. You did not find an edge. You found the combination that best fit the noise of that exact decade. Run it forward and it reverts to mediocre.&lt;/p&gt;

&lt;p&gt;This is just overfitting wearing a backtest costume. Walk-forward analysis is the honest answer: optimize on a window, test on the next unseen window, roll forward, and report only the out-of-sample results.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Survivorship bias in the universe
&lt;/h2&gt;

&lt;p&gt;You backtest a stock strategy on the instruments that exist today. But the ones that delisted, went to zero, or got acquired are missing. Your universe is pre-filtered for survivors, so your results skew up. You need point-in-time universe data, the list as it was on each date, not the list as it is now.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why you cannot fix this with discipline
&lt;/h2&gt;

&lt;p&gt;The tempting conclusion is "just be careful." That fails, because look-ahead bias is the default, not the exception. Every shortcut leaks. The current bar is right there. The full-series mean is one function call away. Careful breaks down the moment you are tired or moving fast.&lt;/p&gt;

&lt;p&gt;The only thing that holds is architecture. In the engine I work on, a strategy literally cannot reference the forming bar. The evaluation step is causal by construction: it hands a block only the data that existed at that timestamp, so there is nothing future to leak. Anti-repainting is not a checkbox you tick, it is a property of the system that you make impossible to violate. That is also why I built &lt;a href="https://backtrex.com/features/anti-repainting" rel="noopener noreferrer"&gt;the no-code backtesting tool I am building&lt;/a&gt; to enforce &lt;code&gt;close[1]&lt;/code&gt; at the engine level rather than trusting the user to remember.&lt;/p&gt;

&lt;p&gt;If you take one thing from this: a backtest is only as honest as the information boundary it respects. Make that boundary impossible to cross, and the curve you see is the curve you can actually trade.&lt;/p&gt;

&lt;p&gt;What is the worst leak you have shipped to production? Mine was the z-score one. Looked like genius for a week.&lt;/p&gt;

</description>
      <category>trading</category>
      <category>python</category>
      <category>machinelearning</category>
      <category>fintech</category>
    </item>
    <item>
      <title>The hardest part of building a no-code backtester wasn't the backtest. It was the export.</title>
      <dc:creator>Matthieu David</dc:creator>
      <pubDate>Fri, 22 May 2026 14:42:47 +0000</pubDate>
      <link>https://dev.to/matthieu_david_9bfe2b8e4f/the-hardest-part-of-building-a-no-code-backtester-wasnt-the-backtest-it-was-the-export-el6</link>
      <guid>https://dev.to/matthieu_david_9bfe2b8e4f/the-hardest-part-of-building-a-no-code-backtester-wasnt-the-backtest-it-was-the-export-el6</guid>
      <description>&lt;p&gt;Two backtest engines will never agree by default. I learned that the slow way.&lt;/p&gt;

&lt;p&gt;I build a no-code backtester. You drag blocks (indicators, conditions, entries, exits), hit run, and get an equity curve in about 30 seconds on 10 years of minute data. The fun part was never the backtest itself. The part that ate months was the export: turning that visual strategy into Pine Script that, when you paste it into TradingView, produces the same trades my own engine did.&lt;/p&gt;

&lt;p&gt;It almost never did at first. Same rules, same data, two completely different equity curves. If you have ever ported a strategy from one platform to another and watched the numbers fall apart, you already know the feeling.&lt;/p&gt;

&lt;p&gt;Here is what actually causes the divergence, and how I got it under 2%.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why two engines disagree
&lt;/h2&gt;

&lt;p&gt;The naive assumption is that a strategy is a set of rules, and rules are rules. They are not. A strategy is rules plus an execution model, and the execution model is full of decisions nobody writes down.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Bar timing (this is the big one).&lt;/strong&gt; When does a signal fire? On the close of the current, still-forming bar, or on the close of the last confirmed bar? If you evaluate &lt;code&gt;close[0]&lt;/code&gt; (the current bar), your backtest looks amazing and your live results are garbage, because in real time that bar is not closed yet. This is repainting. The only honest answer is to evaluate on &lt;code&gt;close[1]&lt;/code&gt;, the previous confirmed bar. If your engine uses &lt;code&gt;close[0]&lt;/code&gt; and your Pine code uses &lt;code&gt;close[1]&lt;/code&gt; (or the reverse), the two will diverge on every single signal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Indicator warmup and seeding.&lt;/strong&gt; An EMA needs a starting value. Some implementations seed it with the first price, some with an SMA of the first N bars, some with zero. RSI has the same issue with its first average gain and loss. Run the same EMA(200) through two libraries and the first few hundred bars will not match. On a 10-year backtest that tail is small, but if your entries cluster early it matters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Order fill assumptions.&lt;/strong&gt; A market order fired on a bar close: does it fill at that close, or at the next bar open? When stop loss and take profit sit inside the same candle, which one fills first? The candle only gives you O, H, L, C, so you cannot know the real path. You have to pick a rule (worst case: assume the stop hits first) and apply the exact same rule in both engines. Pick differently and your win rate shifts by several points.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Floating point and rounding.&lt;/strong&gt; Tick size, price rounding, position sizing rounded to lots. Tiny per-trade, but it compounds across thousands of trades.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Sessions and gaps.&lt;/strong&gt; Where does a daily bar start in UTC? How do you handle the weekend gap in forex? A one-hour offset in session boundaries silently shifts every intraday signal.&lt;/p&gt;

&lt;p&gt;None of these are bugs. They are unwritten choices, and two engines made them independently.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix: stop trying to match Pine, and define one model both can express
&lt;/h2&gt;

&lt;p&gt;My first instinct was to make my Python engine reproduce TradingView exactly. Wrong direction. Pine is a black box that changes, and you cannot unit test someone else's cloud.&lt;/p&gt;

&lt;p&gt;What worked was the opposite. I defined a single execution model that is the lowest common denominator both engines can express without ambiguity, then forced both sides to obey it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Signals evaluate on confirmed bars only. &lt;code&gt;close[1]&lt;/code&gt;, never &lt;code&gt;close[0]&lt;/code&gt;. The engine enforces this at the system level, so a strategy literally cannot reference the forming bar. Repainting stops being a discipline problem and becomes impossible.&lt;/li&gt;
&lt;li&gt;Market orders fill at the next bar open. No "fill at this close" shortcut.&lt;/li&gt;
&lt;li&gt;Same-candle stop and target resolve worst case first.&lt;/li&gt;
&lt;li&gt;One indicator implementation, with the warmup recurrence written to match Pine's documented behavior (not a generic library default).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once the model is pinned down, the export becomes a compiler problem instead of a guessing game.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deterministic codegen, not string templating
&lt;/h2&gt;

&lt;p&gt;Each visual block maps to a small, pure Pine fragment. An RSI block is the same Pine every time. A "crosses below" condition is the same Pine every time. Strategy export is just composing those fragments in topological order and wiring the inputs. No branching on "what did the user probably mean," because the model already removed the ambiguity.&lt;/p&gt;

&lt;p&gt;That determinism is what makes the result testable, which leads to the only part that actually guarantees anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  The parity harness is the whole product
&lt;/h2&gt;

&lt;p&gt;A "2% divergence guarantee" means nothing if you measure it once by hand. So the real work was a test harness:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Generate a batch of strategies covering the block library (trend, mean reversion, SMC patterns, multi-condition).&lt;/li&gt;
&lt;li&gt;Run each through my engine on a fixed dataset.&lt;/li&gt;
&lt;li&gt;Export each to Pine, run it on the same symbol and timeframe in TradingView, pull the results.&lt;/li&gt;
&lt;li&gt;Assert that trade count, win rate, and final equity diverge by less than 2%. If any strategy breaks the threshold, the build fails.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Most of the early failures were not in the codegen. They were in the model assumptions above. Every time a strategy blew past 2%, it pointed at one more unwritten decision I had not pinned down. The harness was less a test and more a way to find the assumptions I did not know I was making.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I would tell anyone building cross-engine anything
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The rules are the easy part. The execution model is the product.&lt;/li&gt;
&lt;li&gt;Repainting is not a feature you add. It is a default you have to actively forbid. Enforce &lt;code&gt;close[1]&lt;/code&gt; at the system level or your users will footgun themselves.&lt;/li&gt;
&lt;li&gt;Do not chase parity by reverse engineering the other engine. Define a model both can express and make both obey it.&lt;/li&gt;
&lt;li&gt;If you claim a number, gate it in CI. A guarantee you cannot reproduce automatically is marketing, not engineering.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I am building this as a visual tool so traders who cannot code can still get an honest backtest and walk out with clean Pine Script. If you want to see the export side of it, it is here: the &lt;a href="https://backtrex.com" rel="noopener noreferrer"&gt;visual backtester I work on&lt;/a&gt;. But the parity lessons above apply whether you use it or roll your own.&lt;/p&gt;

&lt;p&gt;If you have shipped cross-platform strategy export and solved the same-candle stop/target problem differently, I would genuinely like to hear how. That one still keeps me up.&lt;/p&gt;

</description>
      <category>trading</category>
      <category>python</category>
      <category>webdev</category>
      <category>fintech</category>
    </item>
    <item>
      <title>Why I Built a No-Code Backtesting Engine (And What I Learned About Anti-Repainting)</title>
      <dc:creator>Matthieu David</dc:creator>
      <pubDate>Thu, 02 Apr 2026 14:20:43 +0000</pubDate>
      <link>https://dev.to/matthieu_david_9bfe2b8e4f/why-i-built-a-no-code-backtesting-engine-and-what-i-learned-about-anti-repainting-3n9b</link>
      <guid>https://dev.to/matthieu_david_9bfe2b8e4f/why-i-built-a-no-code-backtesting-engine-and-what-i-learned-about-anti-repainting-3n9b</guid>
      <description>&lt;p&gt;I'm a trader and full-stack dev. I spent 5 years trading Forex, got funded on FTMO, lost it all when market conditions changed. The one thing that could have saved me? Proper backtesting before going live.&lt;/p&gt;

&lt;p&gt;The problem is, backtesting tools suck for non-coders. TradingView requires Pine Script. MetaTrader requires MQL. QuantConnect requires Python/C#. If you're a visual trader using SMC/ICT concepts, you're stuck manually scrolling through charts bar by bar.&lt;/p&gt;

&lt;p&gt;So I built &lt;a href="https://backtrex.com" rel="noopener noreferrer"&gt;Backtrex&lt;/a&gt; - a no-code backtesting platform where you drag visual blocks instead of writing code.&lt;/p&gt;

&lt;h2&gt;
  
  
  The anti-repainting problem nobody talks about
&lt;/h2&gt;

&lt;p&gt;Here's something I discovered while building the backtesting engine: most indicators repaint and nobody warns you about it.&lt;/p&gt;

&lt;p&gt;Repainting means an indicator changes its past values based on future data. Your backtest shows a perfect entry signal, but in live trading that signal never existed at that candle. Your backtest is lying to you.&lt;/p&gt;

&lt;p&gt;Common offenders:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zigzag indicators&lt;/strong&gt; - they redraw when a new pivot forms&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Renko charts&lt;/strong&gt; - bar boundaries shift as new data arrives&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Community Pine Script indicators&lt;/strong&gt; - many use &lt;code&gt;close&lt;/code&gt; (current bar) instead of &lt;code&gt;close[1]&lt;/code&gt; (confirmed bar)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Our engine enforces a simple rule: every indicator only uses &lt;code&gt;close[1]&lt;/code&gt; (the previous confirmed bar). Current bar data is never used in signal generation. This alone eliminates the most common source of false backtest results.&lt;/p&gt;

&lt;h2&gt;
  
  
  The architecture decision that saved us
&lt;/h2&gt;

&lt;p&gt;We considered two approaches for the backtest engine:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Event-driven&lt;/strong&gt; (process each tick/bar sequentially)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vectorized&lt;/strong&gt; (compute all signals at once using numpy arrays)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We went with event-driven despite being slower, because it accurately models what happens in live trading. You can't know the next bar's close when you're deciding to enter on the current bar. Vectorized backtests often introduce subtle lookahead bias.&lt;/p&gt;

&lt;p&gt;The tradeoff: we had to optimize heavily to keep backtests under 30 seconds on 10 years of M1 data. Cython for hot paths, pre-computed indicator caches, and a custom candle aggregation pipeline.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd do differently
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Start with fewer indicators.&lt;/strong&gt; We built 50+ blocks. 80% of users use the same 10. Ship the core fast, add the rest later.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Don't underestimate Pine Script export.&lt;/strong&gt; Users want to deploy on TradingView after validating in our tool. The export parity guarantee (&amp;lt;2% divergence) took 3x longer than expected.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build for the community you're in.&lt;/strong&gt; Our best early users are SMC/ICT traders because nobody else builds native Order Block and FVG detection. Find your niche before going broad.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're interested, the landing page is at &lt;a href="https://backtrex.com" rel="noopener noreferrer"&gt;backtrex.com&lt;/a&gt;. Still in early access, feedback welcome.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Building in public. AMA in the comments.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>buildinpublic</category>
      <category>saas</category>
    </item>
  </channel>
</rss>
