<?xml version="1.0" encoding="UTF-8"?>
<rss  xmlns:atom="http://www.w3.org/2005/Atom" 
      xmlns:media="http://search.yahoo.com/mrss/" 
      xmlns:content="http://purl.org/rss/1.0/modules/content/" 
      xmlns:dc="http://purl.org/dc/elements/1.1/" 
      version="2.0">
<channel>
<title>Kwiz Quants</title>
<link>https://quants.kwizresearch.com/blog/</link>
<atom:link href="https://quants.kwizresearch.com/blog/index.xml" rel="self" type="application/rss+xml"/>
<description>Research and engineering insights from Kwiz Quants on systematic forex trading, statistical validation, MT5 automation, and quantitative finance.</description>
<generator>quarto-1.4.554</generator>
<lastBuildDate>Thu, 23 Apr 2026 00:00:00 GMT</lastBuildDate>
<item>
  <title>African Retail Forex: The Quant Gap Data Science Can Fix</title>
  <dc:creator>Kwiz Computing Technologies</dc:creator>
  <link>https://quants.kwizresearch.com/blog/quant-gap-african-retail-traders-vs-institutional-algorithms-data-science/</link>
  <description><![CDATA[ 




<section id="a-broken-bet-most-traders-dont-know-theyre-making" class="level2">
<h2 class="anchored" data-anchor-id="a-broken-bet-most-traders-dont-know-theyre-making">A Broken Bet Most Traders Don’t Know They’re Making</h2>
<p>A Nairobi trader opens their MT5 terminal at 8 a.m. and stares at a GBP/USD chart, reading candlestick patterns the way a witch doctor reads bones. On the other side of that same trade sits a systematic desk in London running a strategy that was validated on fifteen years of tick data, stress-tested across 500 Monte Carlo paths, and deployed with automated risk controls. The Nairobi trader is not in a market. They are in a statistics problem they have not realised they are losing.</p>
<p>The good news: the tools that desk in London uses are not secret. Most of them are open-source. The barrier is not technology anymore. It is knowing where to start.</p>
</section>
<section id="what-happened-to-kenyas-forex-market-after-2023" class="level2">
<h2 class="anchored" data-anchor-id="what-happened-to-kenyas-forex-market-after-2023">What Happened to Kenya’s Forex Market After 2023</h2>
<p>Kenya’s Central Bank tightened its forex dealer licensing framework in 2023, raising capital requirements and compliance obligations that pushed several local brokers out of the market. The direct effect was predictable: retail traders migrated toward regulated offshore brokers, particularly those regulated by the FCA, FSCA, and CySEC. Platforms like Exness, Pepperstone, and IC Markets saw significant uptake across East Africa.</p>
<p>What did not migrate with those traders was any improvement in method. According to industry data from broker disclosure reports, roughly 70-80% of retail forex accounts lose money in any given quarter. That number holds across regions and has held for years. The offshore move changed the regulatory wrapper but not the underlying problem.</p>
<p>The problem is structure. Institutional desks trade with rules. Most retail traders in Kenya trade with feelings dressed up as rules.</p>
</section>
<section id="the-institutional-edge-is-not-what-you-think" class="level2">
<h2 class="anchored" data-anchor-id="the-institutional-edge-is-not-what-you-think">The Institutional Edge Is Not What You Think</h2>
<p>People assume the institutional edge comes from proprietary data or faster execution. Both matter, but neither is the primary driver of systematic profitability. The real edge is the discipline to test hypotheses rigorously before risking capital on them, then execute without emotional override.</p>
<p>A systematic strategy answers three questions before it goes live. First: does the signal actually exist in historical data, or did I find it by testing enough variations that something was bound to look good? Second: does the strategy work on data it was never trained on? Third: what is the realistic worst-case drawdown, and can I stay solvent through it?</p>
<p>Retail traders skip all three questions. They see a strategy work on a demo account for three weeks and call it validated. A demo account run over three weeks is not evidence of anything. It is a coin flip that landed in your favor.</p>
</section>
<section id="the-tooling-that-closes-the-gap" class="level2">
<h2 class="anchored" data-anchor-id="the-tooling-that-closes-the-gap">The Tooling That Closes the Gap</h2>
<p>Here is the specific stack that makes systematic trading possible without an institutional budget.</p>
<p><strong>Backtesting and signal validation.</strong> The <code>quantstrat</code> package in R provides a full backtesting framework. More importantly, R’s statistical ecosystem allows you to apply proper validation methods. At Kwiz Computing, we build every strategy against <a href="../../blog/posts/deflated-sharpe-ratio/">the Deflated Sharpe Ratio framework</a> before any live testing begins. The DSR adjusts your observed Sharpe Ratio for the number of strategies you tested to find it. If you tested 200 variations of a moving average crossover and picked the best one, your backtest result is almost certainly a false discovery. The DSR tells you whether it is.</p>
<p><strong>Walk-forward validation.</strong> A single in-sample/out-of-sample split is not enough for currency strategies, because forex regimes shift. We use <a href="../../blog/posts/combinatorial-purged-cv/">combinatorial purged cross-validation</a>, a technique from Marcos Lopez de Prado’s work, to test strategies across many non-overlapping time windows without introducing lookahead bias. This is the difference between a strategy that looks good on paper and one that has actually been stress-tested.</p>
<p><strong>Automated execution.</strong> MetaTrader 5 supports algorithmic execution through Expert Advisors (EAs). At Kwiz Quants, our <code>kwizmt5</code> R package and our own <code>KwizStrategyTester</code> EA bridge R-side strategy logic to MT5 order routing — that is what <a href="../../blog/posts/systematic-trading-r/">the Kwiz Quants infrastructure runs on</a>. The R side handles signal generation and risk sizing; MT5 handles order routing. This removes the moment-to-moment discretion that kills most retail accounts.</p>
<p><strong>Risk management as code.</strong> Position sizing based on Kelly fractions or fixed-fractional rules, automatic stop placement, and daily drawdown limits can all be implemented as functions that run before any order is submitted. When risk management is code, it does not flinch. It does not convince itself that “this trade is different.”</p>
</section>
<section id="why-this-matters-specifically-for-african-practitioners" class="level2">
<h2 class="anchored" data-anchor-id="why-this-matters-specifically-for-african-practitioners">Why This Matters Specifically for African Practitioners</h2>
<p>The argument sometimes made is that systematic trading is irrelevant to African markets because our capital bases are smaller and our access to institutional data is limited. This argument is wrong on both counts.</p>
<p>Systematic trading helps small accounts more than large ones, not less. A discretionary trader with a $500 account who blows up on three bad weeks of impulsive trading loses everything. A systematic trader with the same account running a strategy with defined stops and position sizing loses a controlled amount, learns something specific from the drawdown, and adjusts. The discipline compounds.</p>
<p>On data access: forex data is among the most democratised financial data in the world. Tick data for major and minor pairs going back ten or more years is available from brokers, from Dukascopy, and from aggregators. A Nairobi-based quant analyst with an internet connection has access to essentially the same raw price data as a desk in Zurich.</p>
<p>The gap is not access. It is the knowledge that proper validation exists, and the willingness to apply it before going live.</p>
</section>
<section id="where-most-people-get-stuck-and-what-to-do" class="level2">
<h2 class="anchored" data-anchor-id="where-most-people-get-stuck-and-what-to-do">Where Most People Get Stuck (and What to Do)</h2>
<p>The typical journey for a data-literate practitioner who wants to build systematic trading infrastructure goes like this. They read about backtesting, implement something in Python or R, see impressive backtest results, and try it live. It fails. They conclude that systematic trading does not work.</p>
<p>The conclusion is wrong. The workflow was wrong. Specifically, the backtest had one or more of the following problems: it was fit on the same data used to evaluate it, it did not account for transaction costs and slippage, or it was selected from many strategies tested on the same dataset, making the result a statistical artifact rather than a real signal.</p>
<p>Fixing these problems is not complicated. It requires applying the right statistical framework in the right order. Validate the signal with DSR before selection. Use purged cross-validation to test generalization. Paper-trade with realistic costs before going live. The framework is documented, the tools are in R and Python, and the process is repeatable.</p>
<p>The practical starting point is simpler than most people expect. Pick one currency pair you trade. Define one rule-based entry signal. Define exact exit rules. Backtest it on five years of hourly data. Apply the DSR. If it passes, validate with walk-forward testing. If it still passes, size it conservatively and run it on demo for sixty days with automated execution. That process is within reach for anyone with working knowledge of R or Python.</p>
</section>
<section id="the-structural-opportunity" class="level2">
<h2 class="anchored" data-anchor-id="the-structural-opportunity">The Structural Opportunity</h2>
<p>Retail forex trading in Kenya and across East Africa is growing. The post-2023 shift toward offshore regulated brokers has, if anything, accelerated participation, because traders now have access to tighter spreads, more instruments, and better execution than was possible through local dealers.</p>
<p>Almost none of that participation is systematic. That is not a permanent condition. It is a skills gap, and skills gaps close.</p>
<p>The practitioners who close this gap first will have a durable edge, not because systematic trading is guaranteed to win, but because trading without a tested, rule-based framework is almost guaranteed to lose over any meaningful time horizon. The statistics on retail trading outcomes are not ambiguous. They have been consistent for decades across every market that has disclosed them.</p>
<p>The question is not whether data science applies to African retail forex. It clearly does. The question is whether you will be among the practitioners who apply it, or among the ones who continue to hand their capital to the algorithms on the other side of the order book.</p>
<p>Kwiz Quants is launching soon. The infrastructure described in this article is not illustrative. It is what we built and what we run ourselves. We are opening early access to a limited group of practitioners before the public launch so we can gather real feedback from people using it on live markets.</p>
<p>If you want to see the platform in action rather than read about it, register now on the <a href="../../kwiz-quants.html">Kwiz Quants page</a>. Spots in the soft launch are limited.</p>


</section>

 ]]></description>
  <category>Quantitative Finance</category>
  <category>Kwiz Quants</category>
  <category>thought-leadership</category>
  <guid>https://quants.kwizresearch.com/blog/quant-gap-african-retail-traders-vs-institutional-algorithms-data-science/</guid>
  <pubDate>Thu, 23 Apr 2026 00:00:00 GMT</pubDate>
  <media:content url="https://quants.kwizresearch.com/blog/quant-gap-african-retail-traders-vs-institutional-algorithms-data-science/thumbnail.png" medium="image" type="image/png"/>
</item>
<item>
  <title>Backtesting Without Lookahead Bias: Combinatorial Purged Cross-Validation</title>
  <dc:creator>Kwizera Jean</dc:creator>
  <link>https://quants.kwizresearch.com/blog/combinatorial-purged-cv/</link>
  <description><![CDATA[ 




<section id="why-standard-cross-validation-fails-in-finance" class="level2">
<h2 class="anchored" data-anchor-id="why-standard-cross-validation-fails-in-finance">Why Standard Cross-Validation Fails in Finance</h2>
<p>Cross-validation is the gold standard for model evaluation in machine learning. Split your data into folds, train on some, test on others, and you get an unbiased estimate of out-of-sample performance. It works beautifully for i.i.d. data — images, text, tabular datasets where observations are independent.</p>
<p>Financial time series violate this assumption fundamentally. Stock prices, forex rates, and other market data exhibit:</p>
<ul>
<li><strong>Serial correlation</strong> — today’s price depends on yesterday’s</li>
<li><strong>Regime changes</strong> — the statistical properties of returns shift over time</li>
<li><strong>Label leakage</strong> — if your target variable is a forward return, adjacent observations share information</li>
</ul>
<p>When you apply standard k-fold cross-validation to financial data, training folds contain information about test folds. The model “sees” the future through correlated observations near the fold boundaries. The result: backtest performance that looks better than what you’ll achieve in live trading.</p>
</section>
<section id="the-purging-and-embargo-solution" class="level2">
<h2 class="anchored" data-anchor-id="the-purging-and-embargo-solution">The Purging and Embargo Solution</h2>
<p>Marcos Lopez de Prado’s <strong>Combinatorial Purged Cross-Validation (CPCV)</strong> addresses these issues through two mechanisms:</p>
<section id="purging" class="level3">
<h3 class="anchored" data-anchor-id="purging">Purging</h3>
<p>Purging removes observations from the training set that overlap temporally with the test set’s label window. If your strategy predicts 5-day returns, then observations within 5 days of any test-set boundary are excluded from training.</p>
<pre><code>Timeline:  |---Train---|xxxPURGEDxxx|---Test---|xxxPURGEDxxx|---Train---|</code></pre>
<p>This eliminates the most direct form of information leakage: training on data whose label period overlaps with test observations.</p>
</section>
<section id="embargo" class="level3">
<h3 class="anchored" data-anchor-id="embargo">Embargo</h3>
<p>An embargo period extends the purge beyond the strict label window. Even after purging label overlap, serial correlation means that observations just outside the purge zone still carry information about the test period. The embargo adds a buffer (typically 1-2% of the dataset length) to ensure genuine independence.</p>
<pre><code>Timeline:  |---Train---|xxPURGExx|--EMBARGO--|---Test---|--EMBARGO--|xxPURGExx|---Train---|</code></pre>
</section>
</section>
<section id="the-combinatorial-approach" class="level2">
<h2 class="anchored" data-anchor-id="the-combinatorial-approach">The Combinatorial Approach</h2>
<p>Standard walk-forward testing uses the data once: train on the first 80%, test on the last 20%. This is wasteful — you get a single estimate of performance from one specific market regime.</p>
<p>CPCV generates all possible combinations of contiguous training and test groups, subject to purging and embargo constraints. For a dataset split into <em>N</em> groups with <em>k</em> test groups, CPCV produces <img src="https://latex.codecogs.com/png.latex?%5Cbinom%7BN%7D%7Bk%7D"> unique backtest paths.</p>
<p>This gives you:</p>
<ol type="1">
<li><strong>Multiple independent performance estimates</strong> rather than a single point estimate</li>
<li><strong>A distribution of backtest results</strong> that reveals strategy robustness</li>
<li><strong>More efficient use of limited data</strong> — every observation appears in test sets</li>
</ol>
</section>
<section id="r-implementation" class="level2">
<h2 class="anchored" data-anchor-id="r-implementation">R Implementation</h2>
<p>Here is a simplified implementation of the CPCV framework:</p>
<div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb3-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' Generate CPCV train/test splits with purging and embargo</span></span>
<span id="cb3-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#'</span></span>
<span id="cb3-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @param n_obs Number of observations</span></span>
<span id="cb3-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @param n_groups Number of groups to split into</span></span>
<span id="cb3-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @param n_test Number of groups to use as test in each split</span></span>
<span id="cb3-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @param purge_length Number of observations to purge at boundaries</span></span>
<span id="cb3-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @param embargo_pct Embargo as a fraction of dataset length</span></span>
<span id="cb3-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @return List of train/test index pairs</span></span>
<span id="cb3-9">generate_cpcv_splits <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(n_obs, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">n_groups =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">n_test =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>,</span>
<span id="cb3-10">                                  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">purge_length =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">embargo_pct =</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.01</span>) {</span>
<span id="cb3-11"></span>
<span id="cb3-12">  embargo_length <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ceiling</span>(n_obs <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> embargo_pct)</span>
<span id="cb3-13">  group_size <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">floor</span>(n_obs <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> n_groups)</span>
<span id="cb3-14"></span>
<span id="cb3-15">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Generate group boundaries</span></span>
<span id="cb3-16">  groups <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">lapply</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">seq_len</span>(n_groups), <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(g) {</span>
<span id="cb3-17">    start <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> (g <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> group_size <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span></span>
<span id="cb3-18">    end <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">min</span>(g <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> group_size, n_obs)</span>
<span id="cb3-19">    start<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span>end</span>
<span id="cb3-20">  })</span>
<span id="cb3-21"></span>
<span id="cb3-22">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Generate all combinations of test groups</span></span>
<span id="cb3-23">  test_combos <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">combn</span>(n_groups, n_test, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">simplify =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">FALSE</span>)</span>
<span id="cb3-24"></span>
<span id="cb3-25">  splits <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">lapply</span>(test_combos, <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(test_groups) {</span>
<span id="cb3-26">    test_idx <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">unlist</span>(groups[test_groups])</span>
<span id="cb3-27">    train_groups <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">setdiff</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">seq_len</span>(n_groups), test_groups)</span>
<span id="cb3-28">    train_idx <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">unlist</span>(groups[train_groups])</span>
<span id="cb3-29"></span>
<span id="cb3-30">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Apply purging: remove training observations near test boundaries</span></span>
<span id="cb3-31">    test_range <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">range</span>(test_idx)</span>
<span id="cb3-32">    purge_zone <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(</span>
<span id="cb3-33">      (test_range[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>] <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> purge_length)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span>(test_range[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>] <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>),</span>
<span id="cb3-34">      (test_range[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>] <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span>(test_range[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>] <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> purge_length)</span>
<span id="cb3-35">    )</span>
<span id="cb3-36"></span>
<span id="cb3-37">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Apply embargo</span></span>
<span id="cb3-38">    embargo_zone <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(</span>
<span id="cb3-39">      (test_range[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>] <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> purge_length <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> embargo_length)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span>(test_range[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>] <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> purge_length <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>),</span>
<span id="cb3-40">      (test_range[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>] <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> purge_length <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span>(test_range[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>] <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> purge_length <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> embargo_length)</span>
<span id="cb3-41">    )</span>
<span id="cb3-42"></span>
<span id="cb3-43">    exclusion_zone <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">unique</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(purge_zone, embargo_zone))</span>
<span id="cb3-44">    exclusion_zone <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> exclusion_zone[exclusion_zone <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&amp;</span> exclusion_zone <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;=</span> n_obs]</span>
<span id="cb3-45"></span>
<span id="cb3-46">    train_idx <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">setdiff</span>(train_idx, exclusion_zone)</span>
<span id="cb3-47"></span>
<span id="cb3-48">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">train =</span> train_idx, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">test =</span> test_idx)</span>
<span id="cb3-49">  })</span>
<span id="cb3-50"></span>
<span id="cb3-51">  splits</span>
<span id="cb3-52">}</span></code></pre></div>
<section id="applying-cpcv-to-a-strategy" class="level3">
<h3 class="anchored" data-anchor-id="applying-cpcv-to-a-strategy">Applying CPCV to a Strategy</h3>
<div class="sourceCode" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb4-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(dplyr)</span>
<span id="cb4-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(purrr)</span>
<span id="cb4-3"></span>
<span id="cb4-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Generate splits</span></span>
<span id="cb4-5">splits <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">generate_cpcv_splits</span>(</span>
<span id="cb4-6">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">n_obs =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">nrow</span>(market_data),</span>
<span id="cb4-7">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">n_groups =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span>,</span>
<span id="cb4-8">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">n_test =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>,</span>
<span id="cb4-9">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">purge_length =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>,</span>
<span id="cb4-10">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">embargo_pct =</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.02</span></span>
<span id="cb4-11">)</span>
<span id="cb4-12"></span>
<span id="cb4-13"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Evaluate strategy on each split</span></span>
<span id="cb4-14">results <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">map_dfr</span>(splits, <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(split) {</span>
<span id="cb4-15">  train_data <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> market_data[split<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>train, ]</span>
<span id="cb4-16">  test_data <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> market_data[split<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>test, ]</span>
<span id="cb4-17"></span>
<span id="cb4-18">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Fit strategy on training data</span></span>
<span id="cb4-19">  model <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">fit_strategy</span>(train_data)</span>
<span id="cb4-20"></span>
<span id="cb4-21">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Evaluate on test data</span></span>
<span id="cb4-22">  signals <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">predict_signals</span>(model, test_data)</span>
<span id="cb4-23">  returns <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">compute_strategy_returns</span>(signals, test_data)</span>
<span id="cb4-24"></span>
<span id="cb4-25">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tibble</span>(</span>
<span id="cb4-26">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">sharpe =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mean</span>(returns) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sd</span>(returns) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sqrt</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">252</span>),</span>
<span id="cb4-27">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">max_drawdown =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">max_drawdown</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">cumsum</span>(returns)),</span>
<span id="cb4-28">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">n_trades =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sum</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">abs</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">diff</span>(signals)) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)</span>
<span id="cb4-29">  )</span>
<span id="cb4-30">})</span>
<span id="cb4-31"></span>
<span id="cb4-32"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Summary: distribution of out-of-sample performance</span></span>
<span id="cb4-33"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">summary</span>(results<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>sharpe)</span></code></pre></div>
</section>
</section>
<section id="cpcv-vs-other-methods" class="level2">
<h2 class="anchored" data-anchor-id="cpcv-vs-other-methods">CPCV vs Other Methods</h2>
<table class="table">
<thead>
<tr class="header">
<th>Method</th>
<th style="text-align: center;">Lookahead Bias</th>
<th style="text-align: center;">Data Efficiency</th>
<th style="text-align: center;">Multiple Estimates</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>Walk-Forward</td>
<td style="text-align: center;">Low</td>
<td style="text-align: center;">Low</td>
<td style="text-align: center;">No</td>
</tr>
<tr class="even">
<td>Standard k-Fold CV</td>
<td style="text-align: center;"><strong>High</strong></td>
<td style="text-align: center;">High</td>
<td style="text-align: center;">Yes</td>
</tr>
<tr class="odd">
<td>Time-Series Split</td>
<td style="text-align: center;">Low</td>
<td style="text-align: center;">Low</td>
<td style="text-align: center;">Limited</td>
</tr>
<tr class="even">
<td>CPCV</td>
<td style="text-align: center;"><strong>None</strong></td>
<td style="text-align: center;">High</td>
<td style="text-align: center;">Yes</td>
</tr>
</tbody>
</table>
<p>Walk-forward testing avoids lookahead bias but gives you a single estimate from one market regime. Standard CV is efficient but leaks information. CPCV achieves both: no lookahead bias <em>and</em> multiple independent estimates.</p>
</section>
<section id="practical-considerations" class="level2">
<h2 class="anchored" data-anchor-id="practical-considerations">Practical Considerations</h2>
<section id="choosing-parameters" class="level3">
<h3 class="anchored" data-anchor-id="choosing-parameters">Choosing Parameters</h3>
<ul>
<li><strong>n_groups</strong>: More groups = more combinations but smaller test sets. 5-8 groups is typical for multi-year datasets.</li>
<li><strong>n_test</strong>: 2 test groups is the most common choice, providing a good balance between the number of combinations and test set size.</li>
<li><strong>purge_length</strong>: Should match or exceed your strategy’s maximum lookahead window (e.g., if you predict 5-day returns, purge at least 5 observations).</li>
<li><strong>embargo_pct</strong>: 1-2% is typical. Higher for strategies that are more sensitive to serial correlation.</li>
</ul>
</section>
<section id="interpreting-results" class="level3">
<h3 class="anchored" data-anchor-id="interpreting-results">Interpreting Results</h3>
<p>The distribution of Sharpe Ratios across CPCV splits tells you more than any single backtest number:</p>
<ul>
<li><strong>Consistently positive across splits</strong> → Robust strategy with genuine edge</li>
<li><strong>High variance across splits</strong> → Strategy is regime-dependent; proceed with caution</li>
<li><strong>Negative in any splits</strong> → Strategy may not generalise; investigate which market conditions cause failure</li>
</ul>
</section>
</section>
<section id="integration-in-the-kwiz-quants-pipeline" class="level2">
<h2 class="anchored" data-anchor-id="integration-in-the-kwiz-quants-pipeline">Integration in the Kwiz Quants Pipeline</h2>
<p>CPCV is the second validation gate in our pipeline, applied after the Deflated Sharpe Ratio screening. Strategies that pass DSR are subjected to CPCV to verify that their performance generalises across different time periods — not just the specific window that happened to produce the best-looking backtest.</p>
<p>Only strategies that show consistently positive risk-adjusted returns across <em>all</em> CPCV splits proceed to MT5 online backtesting. This ensures that when we deploy a strategy to live trading, we have evidence of robustness, not just a single favourable backtest.</p>


</section>

 ]]></description>
  <category>Quantitative Finance</category>
  <category>R</category>
  <category>Machine Learning</category>
  <category>Backtesting</category>
  <guid>https://quants.kwizresearch.com/blog/combinatorial-purged-cv/</guid>
  <pubDate>Sun, 01 Mar 2026 00:00:00 GMT</pubDate>
</item>
<item>
  <title>The Deflated Sharpe Ratio: Why Most Backtests Lie</title>
  <dc:creator>Kwizera Jean</dc:creator>
  <link>https://quants.kwizresearch.com/blog/deflated-sharpe-ratio/</link>
  <description><![CDATA[ 




<section id="the-multiple-testing-problem-in-quant-finance" class="level2">
<h2 class="anchored" data-anchor-id="the-multiple-testing-problem-in-quant-finance">The Multiple Testing Problem in Quant Finance</h2>
<p>Here is a thought experiment. Generate 1,000 random trading strategies — strategies with no actual predictive power, just noise. Backtest all of them on the same historical data. How many will show a Sharpe Ratio above 1.0?</p>
<p>The answer, depending on the data length and volatility, is typically dozens. Some of these random strategies will look genuinely impressive: strong returns, reasonable drawdowns, plausible-looking equity curves. If you picked the best one and presented it to investors, it would look like a real strategy.</p>
<p>This is the <strong>multiple testing problem</strong>, and it is the single most common reason that backtested strategies fail in live trading. When you test many hypotheses on the same dataset, some will appear significant by chance alone. The more strategies you test, the more false discoveries you produce.</p>
</section>
<section id="why-the-standard-sharpe-ratio-fails" class="level2">
<h2 class="anchored" data-anchor-id="why-the-standard-sharpe-ratio-fails">Why the Standard Sharpe Ratio Fails</h2>
<p>The Sharpe Ratio is the most widely used performance metric in quantitative finance. It measures risk-adjusted returns: the excess return per unit of volatility. A Sharpe Ratio of 1.0 is considered good; 2.0 is excellent.</p>
<p>But the standard Sharpe Ratio has no mechanism to account for how many strategies were tested to find the one being presented. If you tested 500 strategies and are showing the best one, the reported Sharpe Ratio is biased upward — sometimes dramatically so.</p>
<p>This is not a theoretical concern. It is the central challenge in quantitative strategy development, and the primary reason that “signal sellers” and retail strategy vendors consistently fail to deliver in live trading what they promised in backtests.</p>
</section>
<section id="the-deflated-sharpe-ratio-framework" class="level2">
<h2 class="anchored" data-anchor-id="the-deflated-sharpe-ratio-framework">The Deflated Sharpe Ratio Framework</h2>
<p>Marcos Lopez de Prado introduced the <strong>Deflated Sharpe Ratio (DSR)</strong> to address this problem directly. The DSR adjusts the observed Sharpe Ratio for:</p>
<ol type="1">
<li><strong>The number of trials</strong> — how many strategies were tested before selecting this one</li>
<li><strong>Skewness</strong> of the return distribution — asymmetry changes the significance threshold</li>
<li><strong>Kurtosis</strong> of the return distribution — fat tails inflate the apparent Sharpe Ratio</li>
<li><strong>The length of the backtest</strong> — shorter backtests are more susceptible to noise</li>
</ol>
<p>The DSR answers a precise question: <em>given how many strategies I tested and the statistical properties of the returns, what is the probability that this observed Sharpe Ratio is a false discovery?</em></p>
</section>
<section id="implementation-in-r" class="level2">
<h2 class="anchored" data-anchor-id="implementation-in-r">Implementation in R</h2>
<p>The DSR computation requires the observed Sharpe Ratio, the number of independent trials, and the higher moments of the return distribution:</p>
<div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb1-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' Compute the Deflated Sharpe Ratio</span></span>
<span id="cb1-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#'</span></span>
<span id="cb1-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @param observed_sr Observed Sharpe Ratio of the selected strategy</span></span>
<span id="cb1-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @param n_trials Number of strategies tested</span></span>
<span id="cb1-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @param n_obs Number of return observations</span></span>
<span id="cb1-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @param skew Skewness of the return series</span></span>
<span id="cb1-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @param kurt Excess kurtosis of the return series</span></span>
<span id="cb1-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @return p-value: probability that the observed SR is a false discovery</span></span>
<span id="cb1-9">deflated_sharpe_ratio <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(observed_sr, n_trials, n_obs,</span>
<span id="cb1-10">                                  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">skew =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">kurt =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>) {</span>
<span id="cb1-11"></span>
<span id="cb1-12">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Expected maximum SR under the null hypothesis</span></span>
<span id="cb1-13">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># (i.e., what you'd expect the best SR to be from n_trials of pure noise)</span></span>
<span id="cb1-14">  euler_mascheroni <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.5772156649</span></span>
<span id="cb1-15">  expected_max_sr <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sqrt</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">log</span>(n_trials)) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span></span>
<span id="cb1-16">    (<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">log</span>(pi) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> euler_mascheroni) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> (<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sqrt</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">log</span>(n_trials)))</span>
<span id="cb1-17"></span>
<span id="cb1-18">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Standard error of the SR estimate, adjusted for higher moments</span></span>
<span id="cb1-19">  sr_se <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sqrt</span>(</span>
<span id="cb1-20">    (<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> skew <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> observed_sr <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> ((kurt <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> observed_sr<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">^</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> (n_obs <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb1-21">  )</span>
<span id="cb1-22"></span>
<span id="cb1-23">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Test statistic: how many SE above the expected maximum?</span></span>
<span id="cb1-24">  test_stat <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> (observed_sr <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> expected_max_sr) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> sr_se</span>
<span id="cb1-25"></span>
<span id="cb1-26">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># One-sided p-value</span></span>
<span id="cb1-27">  p_value <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">pnorm</span>(test_stat, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">lower.tail =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">FALSE</span>)</span>
<span id="cb1-28"></span>
<span id="cb1-29">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(</span>
<span id="cb1-30">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">observed_sr =</span> observed_sr,</span>
<span id="cb1-31">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">expected_max_sr =</span> expected_max_sr,</span>
<span id="cb1-32">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">p_value =</span> p_value,</span>
<span id="cb1-33">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">is_significant =</span> p_value <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.05</span></span>
<span id="cb1-34">  )</span>
<span id="cb1-35">}</span></code></pre></div>
</section>
<section id="a-simulated-demonstration" class="level2">
<h2 class="anchored" data-anchor-id="a-simulated-demonstration">A Simulated Demonstration</h2>
<p>To illustrate the DSR’s power, let’s generate 200 random strategies and see how many survive:</p>
<div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb2-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(dplyr)</span>
<span id="cb2-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(purrr)</span>
<span id="cb2-3"></span>
<span id="cb2-4"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">set.seed</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">42</span>)</span>
<span id="cb2-5">n_strategies <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">200</span></span>
<span id="cb2-6">n_days <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">500</span></span>
<span id="cb2-7"></span>
<span id="cb2-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Generate random return series (no actual signal)</span></span>
<span id="cb2-9">random_returns <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">matrix</span>(</span>
<span id="cb2-10">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">rnorm</span>(n_strategies <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> n_days, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">mean =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">sd =</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.01</span>),</span>
<span id="cb2-11">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">nrow =</span> n_days, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">ncol =</span> n_strategies</span>
<span id="cb2-12">)</span>
<span id="cb2-13"></span>
<span id="cb2-14"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Compute Sharpe Ratios</span></span>
<span id="cb2-15">sharpe_ratios <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">apply</span>(random_returns, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(r) {</span>
<span id="cb2-16">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mean</span>(r) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sd</span>(r) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sqrt</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">252</span>)  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Annualised</span></span>
<span id="cb2-17">})</span>
<span id="cb2-18"></span>
<span id="cb2-19"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># How many look "good" by naive SR?</span></span>
<span id="cb2-20"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sum</span>(sharpe_ratios <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.0</span>)  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Typically 5-15 strategies</span></span>
<span id="cb2-21"></span>
<span id="cb2-22"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Apply DSR to the best strategy</span></span>
<span id="cb2-23">best_idx <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">which.max</span>(sharpe_ratios)</span>
<span id="cb2-24">best_returns <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> random_returns[, best_idx]</span>
<span id="cb2-25"></span>
<span id="cb2-26">dsr_result <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">deflated_sharpe_ratio</span>(</span>
<span id="cb2-27">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">observed_sr =</span> sharpe_ratios[best_idx],</span>
<span id="cb2-28">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">n_trials =</span> n_strategies,</span>
<span id="cb2-29">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">n_obs =</span> n_days,</span>
<span id="cb2-30">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">skew =</span> moments<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">skewness</span>(best_returns),</span>
<span id="cb2-31">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">kurt =</span> moments<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">kurtosis</span>(best_returns)</span>
<span id="cb2-32">)</span>
<span id="cb2-33"></span>
<span id="cb2-34"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># The DSR will correctly identify this as NOT significant</span></span>
<span id="cb2-35"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># because the high SR is explained by the number of trials</span></span></code></pre></div>
<p>In typical runs, the best random strategy achieves a Sharpe Ratio of 1.5-2.5 — impressive by conventional standards. But the DSR correctly identifies it as a false discovery, because the expected maximum SR from 200 random trials explains the observed value entirely.</p>
</section>
<section id="how-kwiz-quants-uses-dsr" class="level2">
<h2 class="anchored" data-anchor-id="how-kwiz-quants-uses-dsr">How Kwiz Quants Uses DSR</h2>
<p>In the Kwiz Quants validation pipeline, every strategy must pass the DSR test before proceeding to MT5 backtesting. This is the first gate in our multi-layer validation process:</p>
<ol type="1">
<li><strong>DSR screening</strong> — Does the strategy’s Sharpe Ratio survive adjustment for the number of strategies tested? If not, it is discarded regardless of how good the backtest looks.</li>
<li><strong>Combinatorial Purged Cross-Validation</strong> — Does the strategy generalise across non-overlapping time periods without lookahead bias?</li>
<li><strong>MT5 online backtesting</strong> — Does the strategy perform with realistic spreads and slippage?</li>
<li><strong>Demo live trading</strong> — Does the strategy work under real market conditions?</li>
</ol>
<p>The DSR is the cheapest and most powerful filter. It eliminates the majority of false discoveries before they consume expensive testing resources downstream.</p>
</section>
<section id="implications" class="level2">
<h2 class="anchored" data-anchor-id="implications">Implications</h2>
<p>The DSR has a simple but profound implication: <strong>the number of strategies you tried matters as much as the performance of the one you selected.</strong> Any performance report that doesn’t disclose the number of trials is, at best, incomplete and, at worst, misleading.</p>
<p>For retail traders evaluating signal providers or strategy vendors, ask one question: <em>how many strategies did you test before finding this one?</em> If the answer is vague or unavailable, the reported performance is almost certainly inflated by selection bias.</p>
<p>For quantitative researchers, the DSR should be a standard part of every strategy development workflow. It costs almost nothing to compute and prevents the most common source of live trading disappointment.</p>


</section>

 ]]></description>
  <category>Quantitative Finance</category>
  <category>R</category>
  <category>Statistical Testing</category>
  <category>Kwiz Quants</category>
  <guid>https://quants.kwizresearch.com/blog/deflated-sharpe-ratio/</guid>
  <pubDate>Sun, 15 Feb 2026 00:00:00 GMT</pubDate>
</item>
<item>
  <title>Why We Build Systematic Trading Infrastructure in R</title>
  <dc:creator>Kwiz Computing Technologies</dc:creator>
  <link>https://quants.kwizresearch.com/blog/systematic-trading-r/</link>
  <description><![CDATA[ 




<section id="the-case-for-r-in-quant-finance" class="level2">
<h2 class="anchored" data-anchor-id="the-case-for-r-in-quant-finance">The Case for R in Quant Finance</h2>
<p>Python dominates the quant finance conversation, and for good reason — it has excellent libraries, a large community, and strong integration with machine learning frameworks. So why did we build Kwiz Quants primarily in R?</p>
</section>
<section id="statistical-depth" class="level2">
<h2 class="anchored" data-anchor-id="statistical-depth">Statistical Depth</h2>
<p>R’s statistical ecosystem is unmatched. The breadth of packages for time series analysis, financial econometrics, and statistical testing is deeper than any other language. When you’re implementing combinatorial purged cross-validation or computing Deflated Sharpe Ratios, R’s statistical foundations make the work cleaner and more reliable.</p>
</section>
<section id="production-ready-r" class="level2">
<h2 class="anchored" data-anchor-id="production-ready-r">Production-Ready R</h2>
<p>The perception that R is “just for analysis” is outdated. Modern R infrastructure makes production deployment viable: <a href="https://www.rplumber.io/">Plumber</a> for REST APIs, Docker for containerisation, <a href="https://rstudio.github.io/renv/">renv</a> for reproducible environments, and <a href="https://appsilon.github.io/rhino/">Rhino</a> for application architecture.</p>
<p>The key is engineering discipline. We apply the same practices used in any production software stack: modular code with <a href="https://klmr.me/box/">box</a>, 95%+ test coverage with <a href="https://testthat.r-lib.org/">testthat</a>, CI/CD pipelines, and structured logging.</p>
</section>
<section id="the-kwiz-quants-stack" class="level2">
<h2 class="anchored" data-anchor-id="the-kwiz-quants-stack">The Kwiz Quants Stack</h2>
<p>Our trading infrastructure connects R-based strategy engines to MetaTrader 5 execution through our own <code>kwizmt5</code> R package — a dual-protocol bridge (TCP and HTTP) — and the <code>KwizStrategyTester</code> EA, with DuckDB and Parquet for logging and Shiny for monitoring. Each component is containerised, tested, and designed for resilience — atomic writes, hot standby replicas, and versioned snapshots.</p>
<p>R isn’t the easy choice for trading infrastructure. But it’s the right choice for a system where statistical rigour is the core differentiator.</p>


</section>

 ]]></description>
  <category>Quantitative Finance</category>
  <category>R</category>
  <category>Kwiz Quants</category>
  <guid>https://quants.kwizresearch.com/blog/systematic-trading-r/</guid>
  <pubDate>Sat, 10 Jan 2026 00:00:00 GMT</pubDate>
</item>
</channel>
</rss>
