<?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: Quantooox</title>
    <description>The latest articles on DEV Community by Quantooox (@quantooo).</description>
    <link>https://dev.to/quantooo</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4208016%2Fe136c512-1fd5-4b33-a0c9-06f9997cc636.png</url>
      <title>DEV Community: Quantooox</title>
      <link>https://dev.to/quantooo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/quantooo"/>
    <language>en</language>
    <item>
      <title>Walk-forward testing with an embargo: keeping time in order</title>
      <dc:creator>Quantooox</dc:creator>
      <pubDate>Mon, 02 Nov 2026 10:48:25 +0000</pubDate>
      <link>https://dev.to/quantooo/walk-forward-testing-with-an-embargo-keeping-time-in-order-5g2n</link>
      <guid>https://dev.to/quantooo/walk-forward-testing-with-an-embargo-keeping-time-in-order-5g2n</guid>
      <description>&lt;p&gt;&lt;code&gt;train_test_split(X, y, shuffle=True)&lt;/code&gt; is fine for photos of cats. For anything that happens in time, it quietly lets the model learn from the future: Tuesday's training row sits next to Monday's test row, and markets on neighbouring days are related.&lt;/p&gt;

&lt;h2&gt;
  
  
  Walk-forward
&lt;/h2&gt;

&lt;p&gt;Keep time in order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Train on everything up to a date.&lt;/li&gt;
&lt;li&gt;Test on the period right after it.&lt;/li&gt;
&lt;li&gt;Move forward and repeat.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Our schedule for the stock models:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Training window:&lt;/strong&gt; the trailing 756 trading days (about three years).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Refit:&lt;/strong&gt; every 63 trading days (about a quarter).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Label:&lt;/strong&gt; each stock's result versus SPY from the next open to the open 20 trading days later.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The gap (embargo)
&lt;/h2&gt;

&lt;p&gt;There's a second, subtler leak. The label looks 20 days ahead. So the last training rows have labels that overlap with the first test days. The model has, in effect, seen part of the test period's outcome.&lt;/p&gt;

&lt;p&gt;The fix is an &lt;strong&gt;embargo&lt;/strong&gt;: leave a gap of 20 trading days between the end of the training data and the start of the test period, so no training label reaches into the test window.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;walk_forward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dates&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;train&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;756&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;63&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;embargo&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;train&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;embargo&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dates&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;tr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dates&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;embargo&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;train&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;embargo&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;te&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dates&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;te&lt;/span&gt;
        &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What it costs
&lt;/h2&gt;

&lt;p&gt;Fewer training rows, more refits, and results that look worse than a shuffled split. Every one of those is a more honest picture of what would have happened in real time.&lt;/p&gt;

&lt;p&gt;If a time-series result was produced with a random split, treat it as unverified.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;From Quantooo Lab, the makers of &lt;a href="https://quantooo.com/method" rel="noopener noreferrer"&gt;quantooo.com&lt;/a&gt;, a free, open and educational quant lab: every formula, every test and every failure is public. See it here: &lt;a href="https://quantooo.com/method" rel="noopener noreferrer"&gt;https://quantooo.com/method&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Educational, not investment advice.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>python</category>
      <category>datascience</category>
      <category>timeseries</category>
    </item>
    <item>
      <title>Out of how many? Multiple testing and the deflated Sharpe ratio</title>
      <dc:creator>Quantooox</dc:creator>
      <pubDate>Sun, 01 Nov 2026 11:25:18 +0000</pubDate>
      <link>https://dev.to/quantooo/out-of-how-many-multiple-testing-and-the-deflated-sharpe-ratio-3pc8</link>
      <guid>https://dev.to/quantooo/out-of-how-many-multiple-testing-and-the-deflated-sharpe-ratio-3pc8</guid>
      <description>&lt;p&gt;Flip a fair coin 10 times. Getting 8 heads is unusual, about a 5% chance. Now get 100 people to do it. A few of them will hit 8 heads, and none of them has a special coin.&lt;/p&gt;

&lt;p&gt;Backtesting works the same way. Each feature set, model, lookback window and threshold you try is another person flipping coins. The more you try, the better your &lt;em&gt;best&lt;/em&gt; result gets, even if nothing works.&lt;/p&gt;

&lt;h2&gt;
  
  
  The question to ask
&lt;/h2&gt;

&lt;p&gt;When someone shows you a great backtest, ask: &lt;strong&gt;out of how many?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If it's the best of 200 variations, the bar for "impressive" is much higher than if it was the only thing tested.&lt;/p&gt;

&lt;h2&gt;
  
  
  Correcting for it
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;deflated Sharpe ratio&lt;/strong&gt; (Bailey and López de Prado) asks: given how many trials were run, and how much their results varied, what Sharpe ratio would the &lt;em&gt;best&lt;/em&gt; of them reach by luck alone? Then it measures how far the actual best result is above that bar, also accounting for skew and fat tails.&lt;/p&gt;

&lt;p&gt;The idea in plain words:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Count every configuration you tried, including the ones you threw away.&lt;/li&gt;
&lt;li&gt;Estimate how big the best result would be if none of them had skill.&lt;/li&gt;
&lt;li&gt;Only call it a finding if the real best clears that bar with room to spare.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What we do
&lt;/h2&gt;

&lt;p&gt;Every model and setting we try for the stock-ranking models is logged as a trial. The leaderboard shows the trial count next to the results, and the deflated Sharpe of the best one. Across our twelve model families, none cleared the bar.&lt;/p&gt;

&lt;h2&gt;
  
  
  A habit worth keeping
&lt;/h2&gt;

&lt;p&gt;Keep a trial log, even a plain CSV with one row per run: date, config, result. It costs nothing and it's the only honest way to answer "out of how many?", including to yourself.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;From Quantooo Lab, the makers of &lt;a href="https://quantooo.com/models" rel="noopener noreferrer"&gt;quantooo.com&lt;/a&gt;, a free, open and educational quant lab: every formula, every test and every failure is public. See it here: &lt;a href="https://quantooo.com/models" rel="noopener noreferrer"&gt;https://quantooo.com/models&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Educational, not investment advice.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>statistics</category>
      <category>datascience</category>
      <category>machinelearning</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Random referees: two controls every ranking model should beat</title>
      <dc:creator>Quantooox</dc:creator>
      <pubDate>Fri, 30 Oct 2026 11:19:23 +0000</pubDate>
      <link>https://dev.to/quantooo/random-referees-two-controls-every-ranking-model-should-beat-4kko</link>
      <guid>https://dev.to/quantooo/random-referees-two-controls-every-ranking-model-should-beat-4kko</guid>
      <description>&lt;p&gt;A model that ranks stocks with an information coefficient (IC) of 0.02 sounds like something. Is it? The quickest way to find out is to compare it with things that &lt;em&gt;can't&lt;/em&gt; have skill.&lt;/p&gt;

&lt;p&gt;We run two kinds of random referee next to every result.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Shuffled rankings (is the result better than chance?)
&lt;/h2&gt;

&lt;p&gt;Keep everything about the strategy the same (number of stocks, rebalancing dates, costs), but pick names at random. Do it many times. You get a distribution of results that pure luck produces with your exact setup.&lt;/p&gt;

&lt;p&gt;Two flavours are useful:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cross-sectional shuffle:&lt;/strong&gt; each day, shuffle scores across stocks. Tests whether the ranking itself carries
information.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time-block shuffle:&lt;/strong&gt; shuffle whole blocks of dates. Keeps the time structure, so it catches results that come
from one lucky period.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your model sits inside the cloud of random results, it hasn't shown skill, whatever the headline number.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Shuffled labels (does the pipeline leak?)
&lt;/h2&gt;

&lt;p&gt;Train the full model, with the same features, the same walk-forward schedule, the same everything, on labels that have been randomly shuffled. There is nothing real to learn. The IC should come out around zero.&lt;/p&gt;

&lt;p&gt;If it doesn't, something in your pipeline is leaking the answer: a feature computed with future data, an overlapping window, a join on the wrong date.&lt;/p&gt;

&lt;p&gt;For our LightGBM model, the shuffled-label version scored about zero, which is what you want to see.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we found
&lt;/h2&gt;

&lt;p&gt;Our score and the twelve model families we tried didn't clearly beat the random referees. Seeing that side by side is more useful than any single number: it shows how big "nothing" looks in this data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;Before you trust any ranking model, ask two questions: how does it compare with random picks, and what does it score on shuffled labels? If nobody ran those, the result isn't finished.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;From Quantooo Lab, the makers of &lt;a href="https://quantooo.com/research" rel="noopener noreferrer"&gt;quantooo.com&lt;/a&gt;, a free, open and educational quant lab: every formula, every test and every failure is public. See it here: &lt;a href="https://quantooo.com/research" rel="noopener noreferrer"&gt;https://quantooo.com/research&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Educational, not investment advice.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>datascience</category>
      <category>python</category>
      <category>statistics</category>
    </item>
    <item>
      <title>The sealed holdout: why we looked at our test data exactly once</title>
      <dc:creator>Quantooox</dc:creator>
      <pubDate>Thu, 29 Oct 2026 11:17:53 +0000</pubDate>
      <link>https://dev.to/quantooo/the-sealed-holdout-why-we-looked-at-our-test-data-exactly-once-2gj2</link>
      <guid>https://dev.to/quantooo/the-sealed-holdout-why-we-looked-at-our-test-data-exactly-once-2gj2</guid>
      <description>&lt;p&gt;Here's a loop most of us have run:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Build a model.&lt;/li&gt;
&lt;li&gt;Check it on the test set.&lt;/li&gt;
&lt;li&gt;Not great. Tweak a feature.&lt;/li&gt;
&lt;li&gt;Check again.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After twenty rounds the test score looks good. But you've been fitting the test set, one decision at a time. The number no longer estimates how the model does on new data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sealing it
&lt;/h2&gt;

&lt;p&gt;For our stock score we did something stricter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Development data (2020–2025):&lt;/strong&gt; build, explore, change things freely.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Final year:&lt;/strong&gt; sealed. Nobody looks at it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Freeze:&lt;/strong&gt; every rule written down and versioned (&lt;code&gt;v1&lt;/code&gt;) before the seal is broken: features, weights, the
universe, the measure, the significance test.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open once.&lt;/strong&gt; Report whatever comes out.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ours came out &lt;em&gt;negative&lt;/em&gt; (IC −0.0585, t −2.47). We published it, and we did not try a "v1.1" on the same year. A new version has to be announced first and tested on data that arrives after the announcement.&lt;/p&gt;

&lt;h2&gt;
  
  
  What counts as a peek
&lt;/h2&gt;

&lt;p&gt;More than you'd think:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Plotting the holdout to "sanity-check" it.&lt;/li&gt;
&lt;li&gt;Choosing a hyperparameter grid after hearing how a colleague's model did on it.&lt;/li&gt;
&lt;li&gt;Re-running with a different random seed because the first one looked unlucky.&lt;/li&gt;
&lt;li&gt;Dropping a stock from the holdout because "its data looked odd".&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each one leaks a little information from the test set into your choices.&lt;/p&gt;

&lt;h2&gt;
  
  
  A cheap version for your own projects
&lt;/h2&gt;

&lt;p&gt;You don't need special tooling:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Put the holdout in a separate file, and don't load it in your notebooks.&lt;/li&gt;
&lt;li&gt;Write the frozen config to a file and commit it (a hash in git is a timestamp).&lt;/li&gt;
&lt;li&gt;Write the evaluation script before you run it, and run it once.&lt;/li&gt;
&lt;li&gt;Record the result even if you hate it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's uncomfortable. It's also the only way your test number means what you think it means.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;From Quantooo Lab, the makers of &lt;a href="https://quantooo.com/method" rel="noopener noreferrer"&gt;quantooo.com&lt;/a&gt;, a free, open and educational quant lab: every formula, every test and every failure is public. See it here: &lt;a href="https://quantooo.com/method" rel="noopener noreferrer"&gt;https://quantooo.com/method&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Educational, not investment advice.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>datascience</category>
      <category>statistics</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Look-ahead bias: the backtest bug that uses tomorrow's newspaper</title>
      <dc:creator>Quantooox</dc:creator>
      <pubDate>Wed, 28 Oct 2026 11:17:01 +0000</pubDate>
      <link>https://dev.to/quantooo/look-ahead-bias-the-backtest-bug-that-uses-tomorrows-newspaper-4ojj</link>
      <guid>https://dev.to/quantooo/look-ahead-bias-the-backtest-bug-that-uses-tomorrows-newspaper-4ojj</guid>
      <description>&lt;p&gt;Imagine testing a strategy for January 2024 using the unemployment rate &lt;em&gt;for&lt;/em&gt; January 2024. Sounds fine, until you remember that number was published in early February. Your backtest just read tomorrow's newspaper.&lt;/p&gt;

&lt;p&gt;That's &lt;strong&gt;look-ahead bias&lt;/strong&gt;: using information that didn't exist yet at the moment of the decision.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it hides
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Economic data.&lt;/strong&gt; Most series are indexed by the period they describe, not the day they were released. The gap is
often weeks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Company reports.&lt;/strong&gt; A quarter ends on 31 March; the filing may arrive in May. Joining on the quarter-end date leaks
six weeks of the future.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Revisions.&lt;/strong&gt; The GDP number you download today is not the one people saw at the time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Index membership.&lt;/strong&gt; Using today's list of index members for a past date (that one has its own name: survivorship
bias).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The fix: store when you &lt;em&gt;knew&lt;/em&gt; it
&lt;/h2&gt;

&lt;p&gt;For every number we keep two dates: the period it describes, and the moment it became public. Then every join is an "as of" join on the publication time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pandas&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;

&lt;span class="c1"&gt;# facts: one row per value, with the time it was actually published
&lt;/span&gt;&lt;span class="n"&gt;facts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;facts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort_values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;published_at&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;days&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;days&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort_values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;decision_time&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# for each decision, take the latest value published *before* it
&lt;/span&gt;&lt;span class="n"&gt;known&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;merge_asof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;days&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;facts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;left_on&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;decision_time&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;right_on&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;published_at&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                      &lt;span class="n"&gt;by&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ticker&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;direction&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;backward&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For US company filings we use the SEC's acceptance timestamp. (Gotcha we hit: in the SEC submissions feed, that timestamp is Eastern time but marked as UTC, so it has to be corrected.) For economic series we use release calendars. Where a release date is unknown, we apply a conservative lag rule rather than the period date.&lt;/p&gt;

&lt;h2&gt;
  
  
  The cost
&lt;/h2&gt;

&lt;p&gt;Doing this makes backtests look worse. Usually noticeably worse. That's the point: the "better" version was never achievable in real time.&lt;/p&gt;

&lt;p&gt;If a backtest's author can't tell you when each input was published, assume it leaks.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;From Quantooo Lab, the makers of &lt;a href="https://quantooo.com/economy" rel="noopener noreferrer"&gt;quantooo.com&lt;/a&gt;, a free, open and educational quant lab: every formula, every test and every failure is public. See it here: &lt;a href="https://quantooo.com/economy" rel="noopener noreferrer"&gt;https://quantooo.com/economy&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Educational, not investment advice.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>datascience</category>
      <category>python</category>
      <category>statistics</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Our stock score ranked stocks backwards on the one test that mattered</title>
      <dc:creator>Quantooox</dc:creator>
      <pubDate>Tue, 27 Oct 2026 11:16:51 +0000</pubDate>
      <link>https://dev.to/quantooo/our-stock-score-ranked-stocks-backwards-on-the-one-test-that-mattered-abo</link>
      <guid>https://dev.to/quantooo/our-stock-score-ranked-stocks-backwards-on-the-one-test-that-mattered-abo</guid>
      <description>&lt;p&gt;We built a simple score for about 100 large US stocks. It combines five things: trend, stability, company fundamentals, the market backdrop and news tone. Each part is a percentile against the other stocks on the same day, and the five parts are averaged with equal weights.&lt;/p&gt;

&lt;p&gt;The question we cared about: do stocks with a higher score do better over the next month than stocks with a lower one?&lt;/p&gt;

&lt;h2&gt;
  
  
  The test
&lt;/h2&gt;

&lt;p&gt;We split the data in two:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Development, 2020–2025.&lt;/strong&gt; This is where we built and checked the score.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A sealed final year.&lt;/strong&gt; Locked away. We froze every rule first, then opened it once.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The measure is the &lt;strong&gt;information coefficient (IC)&lt;/strong&gt;: the rank correlation between today's scores and each stock's result over the following month, relative to the S&amp;amp;P 500. Zero means no skill.&lt;/p&gt;

&lt;h2&gt;
  
  
  The result
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Period&lt;/th&gt;
&lt;th&gt;IC&lt;/th&gt;
&lt;th&gt;t-statistic&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Development&lt;/td&gt;
&lt;td&gt;+0.0085&lt;/td&gt;
&lt;td&gt;0.46&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sealed year&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;−0.0585&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;−2.47&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In development the score was indistinguishable from random. On the sealed year it was significantly &lt;em&gt;negative&lt;/em&gt;: the stocks the score liked tended to lag.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we did with it
&lt;/h2&gt;

&lt;p&gt;We published it, and we changed how the site talks about the score. It now says plainly: the score &lt;em&gt;describes&lt;/em&gt; a stock; it does not &lt;em&gt;predict&lt;/em&gt; it. The bands on the site read "above / around / below average", never anything that sounds like a recommendation.&lt;/p&gt;

&lt;p&gt;We also did not reopen the sealed year to try a better version. That's the whole point of sealing it: one look, one answer. A second look turns the test set into a training set.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why a negative result is worth sharing
&lt;/h2&gt;

&lt;p&gt;Most backtests you see were the best of many attempts, checked on data the author had already looked at. A result that survives that process tells you very little. A frozen rule tested once on unseen data, published whatever it says, tells you a lot, even when the answer is "no".&lt;/p&gt;

&lt;p&gt;The full table, the controls and the code are public.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;From Quantooo Lab, the makers of &lt;a href="https://quantooo.com/research" rel="noopener noreferrer"&gt;quantooo.com&lt;/a&gt;, a free, open and educational quant lab: every formula, every test and every failure is public. See it here: &lt;a href="https://quantooo.com/research" rel="noopener noreferrer"&gt;https://quantooo.com/research&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Educational, not investment advice.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>datascience</category>
      <category>statistics</category>
      <category>machinelearning</category>
      <category>python</category>
    </item>
    <item>
      <title>We tested 12 machine-learning models on the stock market. None of them worked, and we published everything.</title>
      <dc:creator>Quantooox</dc:creator>
      <pubDate>Mon, 26 Oct 2026 11:15:33 +0000</pubDate>
      <link>https://dev.to/quantooo/we-tested-12-machine-learning-models-on-the-stock-market-none-of-them-worked-and-we-published-882</link>
      <guid>https://dev.to/quantooo/we-tested-12-machine-learning-models-on-the-stock-market-none-of-them-worked-and-we-published-882</guid>
      <description>&lt;p&gt;Most finance content shows you what worked. We built a free site, quantooo.com, to do the opposite: test ideas properly, in public, and publish the result whether it is good or bad.&lt;/p&gt;

&lt;p&gt;Here is what we found, and how we tested it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The setup
&lt;/h2&gt;

&lt;p&gt;The site gives about 100 large US stocks (and the Nifty 50 in India) a score from 0 to 100, built from five plain measures: trend, stability, company fundamentals from official filings, the market backdrop, and the tone of the news. Every step of every score is shown on the page, with its source and date.&lt;/p&gt;

&lt;p&gt;The question was simple: do stocks with a higher score do better over the next month than stocks with a lower one?&lt;/p&gt;

&lt;h2&gt;
  
  
  How we tested it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Only the past.&lt;/strong&gt; Every model was trained on earlier data and judged on later data it had never seen   (walk-forward testing).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A sealed final year.&lt;/strong&gt; The last 12 months were locked away. We froze the rules first, then opened that year once.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Random referees.&lt;/strong&gt; Every result was compared with random rankings. If a coin flip does as well, the model isn't   smart.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Counting every try.&lt;/strong&gt; Try 100 ideas and one will look brilliant by luck, so every configuration we tried is counted   and the results are corrected for it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What we found
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The score:&lt;/strong&gt; in development (2020–2025) its link with next-month results was +0.0085, statistically indistinguishable from random (t = 0.46). On the sealed final year it was &lt;strong&gt;−0.0585 (t = −2.47)&lt;/strong&gt;: higher-scored stocks actually did &lt;em&gt;worse&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Twelve model families&lt;/strong&gt; went through the same test: simple rules, ridge regression, LightGBM, neural networks, kNN, LSTM, GRU, a regime-switching model and more. &lt;strong&gt;None showed a significant edge.&lt;/strong&gt; As a check on the pipeline, a model trained on shuffled labels scored about zero, so the test isn't leaking information.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;India,&lt;/strong&gt; same method, with company results used only from the minute they were published: +0.009 (t = 0.21), no skill either.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one thing that helped, a little
&lt;/h2&gt;

&lt;p&gt;Predicting &lt;em&gt;direction&lt;/em&gt; failed. Predicting &lt;em&gt;turbulence&lt;/em&gt; did a bit better. A volatility model for the S&amp;amp;P 500 had lower forecast error than the standard HAR model on 2025–26 (QLIKE 0.816 vs 1.047, p = 0.047). In an exploratory follow-up, the decades-old GARCH model did best of all.&lt;/p&gt;

&lt;p&gt;That matches the research paper the site is built on, &lt;em&gt;Where Machine Learning Helps&lt;/em&gt;: machine learning is far more useful for measuring risk than for picking winners.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why publish a failure?
&lt;/h2&gt;

&lt;p&gt;Because a result you only hear about when it works tells you nothing. Every night the scores are saved &lt;em&gt;before&lt;/em&gt; the market opens, fingerprinted, and timestamped on the Bitcoin blockchain, so nobody, including us, can quietly edit them later. Mistakes go into a public corrections log.&lt;/p&gt;

&lt;p&gt;We are also running hypothetical paper portfolios for one year from 19 October 2026, with rules registered before the first trade. We'll report the result either way. (Hypothetical, not a claim of returns; they could easily lose.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Check our work
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The leaderboard of all 12 models: &lt;a href="https://quantooo.com/models" rel="noopener noreferrer"&gt;https://quantooo.com/models&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The backtest, controls and the sealed year: &lt;a href="https://quantooo.com/research" rel="noopener noreferrer"&gt;https://quantooo.com/research&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Every formula: &lt;a href="https://quantooo.com/method" rel="noopener noreferrer"&gt;https://quantooo.com/method&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The track record nobody can edit: &lt;a href="https://quantooo.com/track-record" rel="noopener noreferrer"&gt;https://quantooo.com/track-record&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's free, with no ads, no fees and no broker links. If you see a flaw in the method, we'd genuinely like to hear it in the comments.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Written by Quantooo Lab, the makers of quantooo.com. Educational, not investment advice.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>datascience</category>
      <category>statistics</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
