<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://thierrymoudiki.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://thierrymoudiki.github.io/" rel="alternate" type="text/html" /><updated>2026-04-12T16:53:50+00:00</updated><id>https://thierrymoudiki.github.io/feed.xml</id><title type="html">Thierry Moudiki’s webpage</title><subtitle>Personal webpage: </subtitle><entry><title type="html">`mlS3` — A Unified S3 Machine Learning Interface in R</title><link href="https://thierrymoudiki.github.io/blog/2026/04/12/r/intro-mlS3" rel="alternate" type="text/html" title="`mlS3` — A Unified S3 Machine Learning Interface in R" /><published>2026-04-12T00:00:00+00:00</published><updated>2026-04-12T00:00:00+00:00</updated><id>https://thierrymoudiki.github.io/blog/2026/04/12/r/intro-mlS3</id><content type="html" xml:base="https://thierrymoudiki.github.io/blog/2026/04/12/r/intro-mlS3"><![CDATA[<h2 id="overview">Overview</h2>

<p>Following the <a href="https://thierrymoudiki.github.io/blog/2026/04/04/r/more-unifiedml-classifiers">R6 object-based package <code class="language-plaintext highlighter-rouge">unifiedml</code> introduced last week</a>, this blog post introduces the <a href="https://github.com/Techtonique/mlS3"><code class="language-plaintext highlighter-rouge">mlS3</code></a> R package, which strives to provide a <strong>unified, consistent <a href="https://adv-r.hadley.nz/s3.html">S3 interface</a></strong> for training and predicting from a variety of popular machine learning models. Rather than learning the idiosyncratic API of each package (you’d still need to read their docs to see the parameters specification, though), <code class="language-plaintext highlighter-rouge">mlS3</code> wraps them under a common <code class="language-plaintext highlighter-rouge">wrap_*</code> / <code class="language-plaintext highlighter-rouge">predict()</code> pattern.</p>

<h2 id="what-youll-learn">What You’ll Learn</h2>

<ul>
  <li>How to install and load <code class="language-plaintext highlighter-rouge">mlS3</code> (for now, from GitHub)</li>
  <li>How to apply a consistent API across multiple ML algorithms for both <strong>classification</strong> and <strong>regression</strong> tasks</li>
</ul>

<h2 id="models-covered">Models Covered</h2>

<table>
  <thead>
    <tr>
      <th>Wrapper</th>
      <th>Underlying Package</th>
      <th>Task(s)</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">wrap_glmnet()</code></td>
      <td><code class="language-plaintext highlighter-rouge">glmnet</code> generalized linear models</td>
      <td>Classification, Regression</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">wrap_lightgbm()</code></td>
      <td><code class="language-plaintext highlighter-rouge">lightgbm</code> gradient boosting</td>
      <td>Classification, Regression</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">wrap_ranger()</code></td>
      <td><code class="language-plaintext highlighter-rouge">ranger</code> random forest</td>
      <td>Classification, Regression</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">wrap_svm()</code></td>
      <td><code class="language-plaintext highlighter-rouge">e1071</code> support vector machines</td>
      <td>Classification, Regression</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">wrap_caret()</code></td>
      <td><code class="language-plaintext highlighter-rouge">caret</code> package</td>
      <td>Classification, Regression with caret <strong>200+ models</strong></td>
    </tr>
  </tbody>
</table>

<h2 id="datasets-used">Datasets Used</h2>

<ul>
  <li><strong><code class="language-plaintext highlighter-rouge">iris</code></strong> — binary and multiclass classification (setosa/versicolor, all three species)</li>
  <li><strong><code class="language-plaintext highlighter-rouge">mtcars</code></strong> — regression to predict miles per gallon (<code class="language-plaintext highlighter-rouge">mpg</code>)</li>
</ul>

<h2 id="key-design-principle">Key Design Principle</h2>

<p>All models follow the same two-step workflow:</p>

<div class="language-R highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">mod</span><span class="w">  </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">wrap_</span><span class="o">*</span><span class="p">(</span><span class="n">X_train</span><span class="p">,</span><span class="w"> </span><span class="n">y_train</span><span class="p">,</span><span class="w"> </span><span class="n">...</span><span class="p">)</span><span class="w">       </span><span class="c1"># Train</span><span class="w">
</span><span class="n">pred</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">predict</span><span class="p">(</span><span class="n">mod</span><span class="p">,</span><span class="w"> </span><span class="n">newx</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">X_test</span><span class="p">,</span><span class="w"> </span><span class="n">...</span><span class="p">)</span><span class="w">    </span><span class="c1"># Predict</span><span class="w">
</span></code></pre></div></div>
<p>This makes it easy to swap algorithms and compare results without rewriting your pipeline.</p>

<h2 id="prerequisites">Prerequisites</h2>

<ul>
  <li>R with the following packages: <code class="language-plaintext highlighter-rouge">remotes</code>, <code class="language-plaintext highlighter-rouge">caret</code>, <code class="language-plaintext highlighter-rouge">randomForest</code>, <code class="language-plaintext highlighter-rouge">ggplot2</code></li>
  <li><code class="language-plaintext highlighter-rouge">mlS3</code> installed from GitHub (for now) via <code class="language-plaintext highlighter-rouge">remotes::install_github("Techtonique/mlS3")</code></li>
</ul>

<h2 id="code">Code</h2>

<h3 id="install-packages">Install packages</h3>

<div class="language-R highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">install.packages</span><span class="p">(</span><span class="nf">c</span><span class="p">(</span><span class="s2">"remotes"</span><span class="p">))</span><span class="w">
</span></code></pre></div></div>

<div class="language-R highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">install.packages</span><span class="p">(</span><span class="nf">c</span><span class="p">(</span><span class="s2">"caret"</span><span class="p">))</span><span class="w">
</span></code></pre></div></div>

<div class="language-R highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">install.packages</span><span class="p">(</span><span class="nf">c</span><span class="p">(</span><span class="s2">"randomForest"</span><span class="p">))</span><span class="w">
</span></code></pre></div></div>

<div class="language-R highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">remotes</span><span class="o">::</span><span class="n">install_github</span><span class="p">(</span><span class="s2">"Techtonique/mlS3"</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<h3 id="predefined-wrappers">Predefined wrappers</h3>

<div class="language-R highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Classification</span><span class="w">

</span><span class="n">library</span><span class="p">(</span><span class="n">mlS3</span><span class="p">)</span><span class="w">

</span><span class="c1"># =============================================================================</span><span class="w">
</span><span class="c1"># Classification examples (no leakage)</span><span class="w">
</span><span class="c1"># =============================================================================</span><span class="w">
</span><span class="n">set.seed</span><span class="p">(</span><span class="m">123</span><span class="p">)</span><span class="w">

</span><span class="c1"># --- Binary classification: iris setosa vs versicolor ---</span><span class="w">
</span><span class="n">iris_bin</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">iris</span><span class="p">[</span><span class="n">iris</span><span class="o">$</span><span class="n">Species</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="s2">"virginica"</span><span class="p">,</span><span class="w"> </span><span class="p">]</span><span class="w">
</span><span class="n">X_bin</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">iris_bin</span><span class="p">[,</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="m">4</span><span class="p">]</span><span class="w">
</span><span class="n">y_bin</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">droplevels</span><span class="p">(</span><span class="n">iris_bin</span><span class="o">$</span><span class="n">Species</span><span class="p">)</span><span class="w">

</span><span class="c1"># Split into train/test</span><span class="w">
</span><span class="n">idx_bin</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">sample</span><span class="p">(</span><span class="n">nrow</span><span class="p">(</span><span class="n">X_bin</span><span class="p">),</span><span class="w"> </span><span class="m">0.7</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">nrow</span><span class="p">(</span><span class="n">X_bin</span><span class="p">))</span><span class="w">
</span><span class="n">X_bin_train</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">X_bin</span><span class="p">[</span><span class="n">idx_bin</span><span class="p">,</span><span class="w"> </span><span class="p">]</span><span class="w">
</span><span class="n">y_bin_train</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">y_bin</span><span class="p">[</span><span class="n">idx_bin</span><span class="p">]</span><span class="w">
</span><span class="n">X_bin_test</span><span class="w">  </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">X_bin</span><span class="p">[</span><span class="o">-</span><span class="n">idx_bin</span><span class="p">,</span><span class="w"> </span><span class="p">]</span><span class="w">
</span><span class="n">y_bin_test</span><span class="w">  </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">y_bin</span><span class="p">[</span><span class="o">-</span><span class="n">idx_bin</span><span class="p">]</span><span class="w">

</span><span class="c1"># glmnet</span><span class="w">
</span><span class="n">mod</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">wrap_glmnet</span><span class="p">(</span><span class="n">X_bin_train</span><span class="p">,</span><span class="w"> </span><span class="n">y_bin_train</span><span class="p">,</span><span class="w"> </span><span class="n">family</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"binomial"</span><span class="p">)</span><span class="w">
</span><span class="n">pred_bin_glmnet</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">predict</span><span class="p">(</span><span class="n">mod</span><span class="p">,</span><span class="w"> </span><span class="n">newx</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">X_bin_test</span><span class="p">,</span><span class="w"> </span><span class="n">type</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"class"</span><span class="p">)</span><span class="w">
</span><span class="n">acc_glmnet</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">mean</span><span class="p">(</span><span class="n">pred_bin_glmnet</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">y_bin_test</span><span class="p">)</span><span class="w">

</span><span class="n">cat</span><span class="p">(</span><span class="s2">"Accuracy (glmnet): "</span><span class="p">,</span><span class="w"> </span><span class="n">acc_glmnet</span><span class="p">,</span><span class="w"> </span><span class="s2">"\n"</span><span class="p">)</span><span class="w">


</span><span class="c1"># --- Multiclass classification: iris all species ---</span><span class="w">
</span><span class="n">X_multi</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">iris</span><span class="p">[,</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="m">4</span><span class="p">]</span><span class="w">
</span><span class="n">y_multi</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">iris</span><span class="o">$</span><span class="n">Species</span><span class="w">

</span><span class="c1"># Split into train/test</span><span class="w">
</span><span class="n">idx_multi</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">sample</span><span class="p">(</span><span class="n">nrow</span><span class="p">(</span><span class="n">X_multi</span><span class="p">),</span><span class="w"> </span><span class="m">0.7</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">nrow</span><span class="p">(</span><span class="n">X_multi</span><span class="p">))</span><span class="w">
</span><span class="n">X_multi_train</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">X_multi</span><span class="p">[</span><span class="n">idx_multi</span><span class="p">,</span><span class="w"> </span><span class="p">]</span><span class="w">
</span><span class="n">y_multi_train</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">y_multi</span><span class="p">[</span><span class="n">idx_multi</span><span class="p">]</span><span class="w">
</span><span class="n">X_multi_test</span><span class="w">  </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">X_multi</span><span class="p">[</span><span class="o">-</span><span class="n">idx_multi</span><span class="p">,</span><span class="w"> </span><span class="p">]</span><span class="w">
</span><span class="n">y_multi_test</span><span class="w">  </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">y_multi</span><span class="p">[</span><span class="o">-</span><span class="n">idx_multi</span><span class="p">]</span><span class="w">

</span><span class="c1"># lightgbm</span><span class="w">
</span><span class="n">mod</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">wrap_lightgbm</span><span class="p">(</span><span class="n">X_multi_train</span><span class="p">,</span><span class="w"> </span><span class="n">y_multi_train</span><span class="p">,</span><span class="w">
                     </span><span class="n">params</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">list</span><span class="p">(</span><span class="n">objective</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"multiclass"</span><span class="p">,</span><span class="w">
                                   </span><span class="n">num_class</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">3</span><span class="p">,</span><span class="w"> </span><span class="n">verbose</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">-1</span><span class="p">),</span><span class="w">
                     </span><span class="n">nrounds</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">150</span><span class="p">)</span><span class="w">
</span><span class="n">pred_multi_lightgbm</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">predict</span><span class="p">(</span><span class="n">mod</span><span class="p">,</span><span class="w"> </span><span class="n">newx</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">X_multi_test</span><span class="p">,</span><span class="w"> </span><span class="n">type</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"class"</span><span class="p">)</span><span class="w">
</span><span class="n">acc_lightgbm</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">mean</span><span class="p">(</span><span class="n">pred_multi_lightgbm</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">y_multi_test</span><span class="p">)</span><span class="w">

</span><span class="c1"># ranger</span><span class="w">
</span><span class="n">mod</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">wrap_ranger</span><span class="p">(</span><span class="n">X_multi_train</span><span class="p">,</span><span class="w"> </span><span class="n">y_multi_train</span><span class="p">,</span><span class="w"> </span><span class="n">num.trees</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">100L</span><span class="p">)</span><span class="w">
</span><span class="n">pred_multi_ranger</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">predict</span><span class="p">(</span><span class="n">mod</span><span class="p">,</span><span class="w"> </span><span class="n">newx</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">X_multi_test</span><span class="p">,</span><span class="w"> </span><span class="n">type</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"class"</span><span class="p">)</span><span class="w">
</span><span class="n">acc_ranger</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">mean</span><span class="p">(</span><span class="n">pred_multi_ranger</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">y_multi_test</span><span class="p">)</span><span class="w">

</span><span class="c1"># svm</span><span class="w">
</span><span class="n">mod</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">wrap_svm</span><span class="p">(</span><span class="n">X_multi_train</span><span class="p">,</span><span class="w"> </span><span class="n">y_multi_train</span><span class="p">,</span><span class="w"> </span><span class="n">kernel</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"radial"</span><span class="p">)</span><span class="w">
</span><span class="n">pred_multi_svm</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">predict</span><span class="p">(</span><span class="n">mod</span><span class="p">,</span><span class="w"> </span><span class="n">newx</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">X_multi_test</span><span class="p">,</span><span class="w"> </span><span class="n">type</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"class"</span><span class="p">)</span><span class="w">
</span><span class="n">acc_svm</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">mean</span><span class="p">(</span><span class="n">pred_multi_svm</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">y_multi_test</span><span class="p">)</span><span class="w">

</span><span class="n">cat</span><span class="p">(</span><span class="s2">"Accuracy (lightgbm): "</span><span class="p">,</span><span class="w"> </span><span class="n">acc_lightgbm</span><span class="p">,</span><span class="w"> </span><span class="s2">"\n"</span><span class="p">)</span><span class="w">
</span><span class="n">cat</span><span class="p">(</span><span class="s2">"Accuracy (ranger): "</span><span class="p">,</span><span class="w"> </span><span class="n">acc_ranger</span><span class="p">,</span><span class="w"> </span><span class="s2">"\n"</span><span class="p">)</span><span class="w">
</span><span class="n">cat</span><span class="p">(</span><span class="s2">"Accuracy (svm): "</span><span class="p">,</span><span class="w"> </span><span class="n">acc_svm</span><span class="p">,</span><span class="w"> </span><span class="s2">"\n"</span><span class="p">)</span><span class="w">


</span><span class="c1"># Regression</span><span class="w">


</span><span class="c1"># =============================================================================</span><span class="w">
</span><span class="c1"># Regression examples (mtcars)</span><span class="w">
</span><span class="c1"># =============================================================================</span><span class="w">
</span><span class="n">X_reg</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">mtcars</span><span class="p">[,</span><span class="w"> </span><span class="m">-1</span><span class="p">]</span><span class="w">
</span><span class="n">y_reg</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">mtcars</span><span class="o">$</span><span class="n">mpg</span><span class="w">

</span><span class="c1"># Split into train/test</span><span class="w">
</span><span class="n">set.seed</span><span class="p">(</span><span class="m">123</span><span class="p">)</span><span class="w">
</span><span class="n">idx_reg</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">sample</span><span class="p">(</span><span class="n">nrow</span><span class="p">(</span><span class="n">X_reg</span><span class="p">),</span><span class="w"> </span><span class="m">0.7</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">nrow</span><span class="p">(</span><span class="n">X_reg</span><span class="p">))</span><span class="w">
</span><span class="n">X_reg_train</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">X_reg</span><span class="p">[</span><span class="n">idx_reg</span><span class="p">,</span><span class="w"> </span><span class="p">];</span><span class="w">  </span><span class="n">y_reg_train</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">y_reg</span><span class="p">[</span><span class="n">idx_reg</span><span class="p">]</span><span class="w">
</span><span class="n">X_reg_test</span><span class="w">  </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">X_reg</span><span class="p">[</span><span class="o">-</span><span class="n">idx_reg</span><span class="p">,</span><span class="w"> </span><span class="p">];</span><span class="w"> </span><span class="n">y_reg_test</span><span class="w">  </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">y_reg</span><span class="p">[</span><span class="o">-</span><span class="n">idx_reg</span><span class="p">]</span><span class="w">

</span><span class="c1"># lightgbm</span><span class="w">
</span><span class="n">mod</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">wrap_lightgbm</span><span class="p">(</span><span class="n">X_reg_train</span><span class="p">,</span><span class="w"> </span><span class="n">y_reg_train</span><span class="p">,</span><span class="w">
                     </span><span class="n">params</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">list</span><span class="p">(</span><span class="n">objective</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"regression"</span><span class="p">,</span><span class="w"> </span><span class="n">verbose</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">-1</span><span class="p">),</span><span class="w">
                     </span><span class="n">nrounds</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">50</span><span class="p">)</span><span class="w">
</span><span class="n">pred_reg_lightgbm</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">predict</span><span class="p">(</span><span class="n">mod</span><span class="p">,</span><span class="w"> </span><span class="n">newx</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">X_reg_test</span><span class="p">)</span><span class="w">
</span><span class="n">rmse_lightgbm</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">sqrt</span><span class="p">(</span><span class="n">mean</span><span class="p">((</span><span class="n">pred_reg_lightgbm</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">y_reg_test</span><span class="p">)</span><span class="o">^</span><span class="m">2</span><span class="p">))</span><span class="w">

</span><span class="c1"># glmnet</span><span class="w">
</span><span class="n">mod</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">wrap_glmnet</span><span class="p">(</span><span class="n">X_reg_train</span><span class="p">,</span><span class="w"> </span><span class="n">y_reg_train</span><span class="p">,</span><span class="w"> </span><span class="n">alpha</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0</span><span class="p">)</span><span class="w">
</span><span class="n">pred_reg_glmnet</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">predict</span><span class="p">(</span><span class="n">mod</span><span class="p">,</span><span class="w"> </span><span class="n">newx</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">X_reg_test</span><span class="p">)</span><span class="w">
</span><span class="n">rmse_glmnet</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">sqrt</span><span class="p">(</span><span class="n">mean</span><span class="p">((</span><span class="n">pred_reg_glmnet</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">y_reg_test</span><span class="p">)</span><span class="o">^</span><span class="m">2</span><span class="p">))</span><span class="w">

</span><span class="c1"># svm</span><span class="w">
</span><span class="n">mod</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">wrap_svm</span><span class="p">(</span><span class="n">X_reg_train</span><span class="p">,</span><span class="w"> </span><span class="n">y_reg_train</span><span class="p">)</span><span class="w">
</span><span class="n">pred_reg_svm</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">predict</span><span class="p">(</span><span class="n">mod</span><span class="p">,</span><span class="w"> </span><span class="n">newx</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">X_reg_test</span><span class="p">)</span><span class="w">
</span><span class="n">rmse_svm</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">sqrt</span><span class="p">(</span><span class="n">mean</span><span class="p">((</span><span class="n">pred_reg_svm</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">y_reg_test</span><span class="p">)</span><span class="o">^</span><span class="m">2</span><span class="p">))</span><span class="w">

</span><span class="c1"># ranger</span><span class="w">
</span><span class="n">mod</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">wrap_ranger</span><span class="p">(</span><span class="n">X_reg_train</span><span class="p">,</span><span class="w"> </span><span class="n">y_reg_train</span><span class="p">,</span><span class="w"> </span><span class="n">num.trees</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">100L</span><span class="p">)</span><span class="w">
</span><span class="n">pred_reg_ranger</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">predict</span><span class="p">(</span><span class="n">mod</span><span class="p">,</span><span class="w"> </span><span class="n">newx</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">X_reg_test</span><span class="p">)</span><span class="w">
</span><span class="n">rmse_ranger</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">sqrt</span><span class="p">(</span><span class="n">mean</span><span class="p">((</span><span class="n">pred_reg_ranger</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">y_reg_test</span><span class="p">)</span><span class="o">^</span><span class="m">2</span><span class="p">))</span><span class="w">

</span><span class="n">cat</span><span class="p">(</span><span class="s2">"RMSE (lightgbm): "</span><span class="p">,</span><span class="w"> </span><span class="n">rmse_lightgbm</span><span class="p">,</span><span class="w"> </span><span class="s2">"\n"</span><span class="p">)</span><span class="w">
</span><span class="n">cat</span><span class="p">(</span><span class="s2">"RMSE (glmnet): "</span><span class="p">,</span><span class="w"> </span><span class="n">rmse_glmnet</span><span class="p">,</span><span class="w"> </span><span class="s2">"\n"</span><span class="p">)</span><span class="w">
</span><span class="n">cat</span><span class="p">(</span><span class="s2">"RMSE (svm): "</span><span class="p">,</span><span class="w"> </span><span class="n">rmse_svm</span><span class="p">,</span><span class="w"> </span><span class="s2">"\n"</span><span class="p">)</span><span class="w">
</span><span class="n">cat</span><span class="p">(</span><span class="s2">"RMSE (ranger): "</span><span class="p">,</span><span class="w"> </span><span class="n">rmse_ranger</span><span class="p">,</span><span class="w"> </span><span class="s2">"\n"</span><span class="p">)</span><span class="w">


</span></code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Accuracy (glmnet):  1 
Accuracy (lightgbm):  0.2444444  # I'm probably doing something wrong here
Accuracy (ranger):  0.9333333 
Accuracy (svm):  0.9333333 
RMSE (lightgbm):  4.713678 
RMSE (glmnet):  2.972557 
RMSE (svm):  2.275837 
RMSE (ranger):  2.067692 
</code></pre></div></div>

<h3 id="caret-wrapper"><code class="language-plaintext highlighter-rouge">caret</code> wrapper</h3>

<p>For this part, you need to install package <code class="language-plaintext highlighter-rouge">caret</code> and <code class="language-plaintext highlighter-rouge">randomForest</code>. Model parameters available for <code class="language-plaintext highlighter-rouge">caret</code>: <a href="https://topepo.github.io/caret/available-models.html">https://topepo.github.io/caret/available-models.html</a></p>

<div class="language-R highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">library</span><span class="p">(</span><span class="n">mlS3</span><span class="p">)</span><span class="w">
</span><span class="n">library</span><span class="p">(</span><span class="n">caret</span><span class="p">)</span><span class="w">

</span><span class="c1"># ============================================================================</span><span class="w">
</span><span class="c1"># Regression with mtcars dataset</span><span class="w">
</span><span class="c1"># ============================================================================</span><span class="w">
</span><span class="n">data</span><span class="p">(</span><span class="n">mtcars</span><span class="p">)</span><span class="w">

</span><span class="c1"># Prepare data</span><span class="w">
</span><span class="n">X_reg</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">mtcars</span><span class="p">[,</span><span class="w"> </span><span class="m">-1</span><span class="p">]</span><span class="w">  </span><span class="c1"># All except mpg</span><span class="w">
</span><span class="n">y_reg</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">mtcars</span><span class="o">$</span><span class="n">mpg</span><span class="w">     </span><span class="c1"># Target variable</span><span class="w">

</span><span class="c1"># Split into train/test</span><span class="w">
</span><span class="n">set.seed</span><span class="p">(</span><span class="m">123</span><span class="p">)</span><span class="w">
</span><span class="n">idx_reg</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">sample</span><span class="p">(</span><span class="n">nrow</span><span class="p">(</span><span class="n">X_reg</span><span class="p">),</span><span class="w"> </span><span class="m">0.7</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">nrow</span><span class="p">(</span><span class="n">X_reg</span><span class="p">))</span><span class="w">
</span><span class="n">X_reg_train</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">X_reg</span><span class="p">[</span><span class="n">idx_reg</span><span class="p">,</span><span class="w"> </span><span class="p">]</span><span class="w">
</span><span class="n">y_reg_train</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">y_reg</span><span class="p">[</span><span class="n">idx_reg</span><span class="p">]</span><span class="w">
</span><span class="n">X_reg_test</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">X_reg</span><span class="p">[</span><span class="o">-</span><span class="n">idx_reg</span><span class="p">,</span><span class="w"> </span><span class="p">]</span><span class="w">
</span><span class="n">y_reg_test</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">y_reg</span><span class="p">[</span><span class="o">-</span><span class="n">idx_reg</span><span class="p">]</span><span class="w">

</span><span class="c1"># ----------------------------------------------------------------------------</span><span class="w">
</span><span class="c1"># Example 1: Random Forest with specific parameters</span><span class="w">
</span><span class="c1"># ----------------------------------------------------------------------------</span><span class="w">
</span><span class="n">cat</span><span class="p">(</span><span class="s2">"\n=== Example 1: Random Forest Regression ===\n"</span><span class="p">)</span><span class="w">

</span><span class="n">mod_rf</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">wrap_caret</span><span class="p">(</span><span class="n">X_reg_train</span><span class="p">,</span><span class="w"> </span><span class="n">y_reg_train</span><span class="p">,</span><span class="w">
                     </span><span class="n">method</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"rf"</span><span class="p">,</span><span class="w">
                     </span><span class="n">mtry</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">3</span><span class="p">)</span><span class="w">        </span><span class="c1"># Number of variables sampled at each split</span><span class="w">

</span><span class="n">print</span><span class="p">(</span><span class="n">mod_rf</span><span class="p">)</span><span class="w">

</span><span class="c1"># Predictions</span><span class="w">
</span><span class="n">pred_rf</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">predict</span><span class="p">(</span><span class="n">mod_rf</span><span class="p">,</span><span class="w"> </span><span class="n">newx</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">X_reg_test</span><span class="p">)</span><span class="w">
</span><span class="n">rmse_rf</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">sqrt</span><span class="p">(</span><span class="n">mean</span><span class="p">((</span><span class="n">pred_rf</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">y_reg_test</span><span class="p">)</span><span class="o">^</span><span class="m">2</span><span class="p">))</span><span class="w">
</span><span class="n">r2_rf</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">1</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="nf">sum</span><span class="p">((</span><span class="n">y_reg_test</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">pred_rf</span><span class="p">)</span><span class="o">^</span><span class="m">2</span><span class="p">)</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="nf">sum</span><span class="p">((</span><span class="n">y_reg_test</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">mean</span><span class="p">(</span><span class="n">y_reg_test</span><span class="p">))</span><span class="o">^</span><span class="m">2</span><span class="p">)</span><span class="w">

</span><span class="n">cat</span><span class="p">(</span><span class="s2">"RMSE:"</span><span class="p">,</span><span class="w"> </span><span class="nf">round</span><span class="p">(</span><span class="n">rmse_rf</span><span class="p">,</span><span class="w"> </span><span class="m">3</span><span class="p">),</span><span class="w"> </span><span class="s2">"\n"</span><span class="p">)</span><span class="w">
</span><span class="n">cat</span><span class="p">(</span><span class="s2">"R-squared:"</span><span class="p">,</span><span class="w"> </span><span class="nf">round</span><span class="p">(</span><span class="n">r2_rf</span><span class="p">,</span><span class="w"> </span><span class="m">3</span><span class="p">),</span><span class="w"> </span><span class="s2">"\n"</span><span class="p">)</span><span class="w">

</span></code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>=== Example 1: Random Forest Regression ===
$model
Random Forest 

22 samples
10 predictors

No pre-processing
Resampling: None 

$task
[1] "regression"

$method
[1] "rf"

$parameters
$parameters$mtry
[1] 3


attr(,"class")
[1] "wrap_caret"
RMSE: 2.007 
R-squared: 0.681 
</code></pre></div></div>

<div class="language-R highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">library</span><span class="p">(</span><span class="n">ggplot2</span><span class="p">)</span><span class="w">

</span><span class="n">df</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">data.frame</span><span class="p">(</span><span class="w">
  </span><span class="n">pred</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">pred_rf</span><span class="p">,</span><span class="w">
  </span><span class="n">actual</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">y_reg_test</span><span class="w">
</span><span class="p">)</span><span class="w">

</span><span class="n">ggplot</span><span class="p">(</span><span class="n">df</span><span class="p">,</span><span class="w"> </span><span class="n">aes</span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">pred</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">actual</span><span class="p">))</span><span class="w"> </span><span class="o">+</span><span class="w">
  </span><span class="n">geom_point</span><span class="p">()</span><span class="w"> </span><span class="o">+</span><span class="w">
  </span><span class="n">geom_abline</span><span class="p">(</span><span class="n">slope</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">1</span><span class="p">,</span><span class="w"> </span><span class="n">intercept</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0</span><span class="p">,</span><span class="w"> </span><span class="n">color</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"red"</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w">
  </span><span class="n">theme_minimal</span><span class="p">()</span><span class="w"> </span><span class="o">+</span><span class="w">
  </span><span class="n">labs</span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Predicted"</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Actual"</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p><img src="/images/2026-04-12/2026-04-12-intro-mlS3_7_0.png" alt="image-title-here" class="img-responsive" /></p>]]></content><author><name></name></author><category term="R" /><summary type="html"><![CDATA[Introduction to `mlS3` — A Unified S3 Machine Learning Interface in R]]></summary></entry><entry><title type="html">One interface, (Almost) Every Classifier: unifiedml v0.2.1</title><link href="https://thierrymoudiki.github.io/blog/2026/04/04/r/more-unifiedml-classifiers" rel="alternate" type="text/html" title="One interface, (Almost) Every Classifier: unifiedml v0.2.1" /><published>2026-04-04T00:00:00+00:00</published><updated>2026-04-04T00:00:00+00:00</updated><id>https://thierrymoudiki.github.io/blog/2026/04/04/r/more-unifiedml-classifiers</id><content type="html" xml:base="https://thierrymoudiki.github.io/blog/2026/04/04/r/more-unifiedml-classifiers"><![CDATA[<p>A new version of <code class="language-plaintext highlighter-rouge">unifiedml</code> is out; available on CRAN. <code class="language-plaintext highlighter-rouge">unifiedml</code> is an effort to offer a unified interface to R’s machine learning models.</p>

<p>The main change in this version <code class="language-plaintext highlighter-rouge">0.2.1</code> is the removal of <code class="language-plaintext highlighter-rouge">type</code> (of prediction) from <code class="language-plaintext highlighter-rouge">predict</code>, and the use of<code class="language-plaintext highlighter-rouge">...</code> instead, which is more generic and flexible.</p>

<p><strong>This post contains advanced examples of use of <code class="language-plaintext highlighter-rouge">unifiedml</code> for classification</strong>, with <code class="language-plaintext highlighter-rouge">ranger</code> and <code class="language-plaintext highlighter-rouge">xgboost</code>. More examples have been added to <a href="https://cloud.r-project.org/web/packages/unifiedml/vignettes/unifiedml-vignette.html">the package vignettes</a> too.</p>

<div class="language-R highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">install.packages</span><span class="p">(</span><span class="s2">"unifiedml"</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<div class="language-R highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">install.packages</span><span class="p">(</span><span class="nf">c</span><span class="p">(</span><span class="s2">"ranger"</span><span class="p">))</span><span class="w">
</span></code></pre></div></div>

<div class="language-R highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">library</span><span class="p">(</span><span class="s2">"unifiedml"</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Loading required package: doParallel

Loading required package: foreach

Loading required package: iterators

Loading required package: parallel

Loading required package: R6
</code></pre></div></div>

<h1 id="1---ranger-example">1 - <code class="language-plaintext highlighter-rouge">ranger</code> example</h1>

<div class="language-R highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">library</span><span class="p">(</span><span class="n">ranger</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<div class="language-R highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="w">

</span><span class="c1"># 2 - 'ranger' classification ---------------------------</span><span class="w">

</span><span class="c1"># -------------------------------</span><span class="w">
</span><span class="c1"># S3 wrapper for ranger</span><span class="w">
</span><span class="c1"># -------------------------------</span><span class="w">

</span><span class="c1"># Fit function remains the same</span><span class="w">
</span><span class="n">my_ranger</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="p">,</span><span class="w"> </span><span class="n">...</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="o">!</span><span class="n">is.data.frame</span><span class="p">(</span><span class="n">x</span><span class="p">))</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">as.data.frame</span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w">
  </span><span class="n">y</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">as.factor</span><span class="p">(</span><span class="n">y</span><span class="p">)</span><span class="w">
  </span><span class="n">colnames</span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">paste0</span><span class="p">(</span><span class="s2">"X"</span><span class="p">,</span><span class="w"> </span><span class="nf">seq_len</span><span class="p">(</span><span class="n">ncol</span><span class="p">(</span><span class="n">x</span><span class="p">)))</span><span class="w">
  </span><span class="n">df</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">data.frame</span><span class="p">(</span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">y</span><span class="p">,</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w">
  </span><span class="n">fit</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">ranger</span><span class="o">::</span><span class="n">ranger</span><span class="p">(</span><span class="n">y</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">.</span><span class="p">,</span><span class="w"> </span><span class="n">data</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">df</span><span class="p">,</span><span class="w"> </span><span class="n">probability</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">,</span><span class="w"> </span><span class="n">...</span><span class="p">)</span><span class="w">
  </span><span class="n">structure</span><span class="p">(</span><span class="nf">list</span><span class="p">(</span><span class="n">fit</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">fit</span><span class="p">),</span><span class="w"> </span><span class="n">class</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"my_ranger"</span><span class="p">)</span><span class="w">
</span><span class="p">}</span><span class="w">

</span><span class="c1"># Predict only with newdata</span><span class="w">
</span><span class="n">predict.my_ranger</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">object</span><span class="p">,</span><span class="w"> </span><span class="n">newdata</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">NULL</span><span class="p">,</span><span class="w"> </span><span class="n">newx</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">NULL</span><span class="p">,</span><span class="w"> </span><span class="n">...</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="o">!</span><span class="nf">is.null</span><span class="p">(</span><span class="n">newx</span><span class="p">))</span><span class="w"> </span><span class="n">newdata</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">newx</span><span class="w">
  </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nf">is.null</span><span class="p">(</span><span class="n">newdata</span><span class="p">))</span><span class="w"> </span><span class="n">stop</span><span class="p">(</span><span class="s2">"No data provided for prediction"</span><span class="p">)</span><span class="w">
</span><span class="c1">#  misc::debug_print(newx)</span><span class="w">
</span><span class="c1">#  misc::debug_print(newdata)</span><span class="w">
  </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nf">is.matrix</span><span class="p">(</span><span class="n">newdata</span><span class="p">))</span><span class="w"> </span><span class="n">newdata</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">as.data.frame</span><span class="p">(</span><span class="n">newdata</span><span class="p">)</span><span class="w">
</span><span class="c1">#  misc::debug_print(newdata)</span><span class="w">
  </span><span class="c1"># Unconditionally rename to match training</span><span class="w">
  </span><span class="n">colnames</span><span class="p">(</span><span class="n">newdata</span><span class="p">)</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">paste0</span><span class="p">(</span><span class="s2">"X"</span><span class="p">,</span><span class="w"> </span><span class="nf">seq_len</span><span class="p">(</span><span class="n">ncol</span><span class="p">(</span><span class="n">newdata</span><span class="p">)))</span><span class="w">
</span><span class="c1">#  misc::debug_print(newdata)</span><span class="w">
  </span><span class="n">preds</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">predict</span><span class="p">(</span><span class="n">object</span><span class="o">$</span><span class="n">fit</span><span class="p">,</span><span class="w"> </span><span class="n">data</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">newdata</span><span class="p">)</span><span class="o">$</span><span class="n">predictions</span><span class="w">
</span><span class="c1">#  misc::debug_print(newdata)</span><span class="w">
  </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nf">is.matrix</span><span class="p">(</span><span class="n">preds</span><span class="p">)</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="n">ncol</span><span class="p">(</span><span class="n">preds</span><span class="p">)</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="m">2</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="n">lvls</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">colnames</span><span class="p">(</span><span class="n">preds</span><span class="p">)</span><span class="w">
    </span><span class="nf">return</span><span class="p">(</span><span class="n">ifelse</span><span class="p">(</span><span class="n">preds</span><span class="p">[,</span><span class="w"> </span><span class="m">2</span><span class="p">]</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="m">0.5</span><span class="p">,</span><span class="w"> </span><span class="n">lvls</span><span class="p">[</span><span class="m">2</span><span class="p">],</span><span class="w"> </span><span class="n">lvls</span><span class="p">[</span><span class="m">1</span><span class="p">]))</span><span class="w">
  </span><span class="p">}</span><span class="w">

  </span><span class="n">preds</span><span class="w">
</span><span class="p">}</span><span class="w">

</span><span class="c1"># Print method</span><span class="w">
</span><span class="n">print.my_ranger</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">...</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="n">cat</span><span class="p">(</span><span class="s2">"my_ranger model\n"</span><span class="p">)</span><span class="w">
  </span><span class="n">print</span><span class="p">(</span><span class="n">x</span><span class="o">$</span><span class="n">fit</span><span class="p">)</span><span class="w">
</span><span class="p">}</span><span class="w">

</span><span class="c1"># -------------------------------</span><span class="w">
</span><span class="c1"># Example: Iris binary classification</span><span class="w">
</span><span class="c1"># -------------------------------</span><span class="w">

</span><span class="n">set.seed</span><span class="p">(</span><span class="m">123</span><span class="p">)</span><span class="w">
</span><span class="n">iris_binary</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">iris</span><span class="p">[</span><span class="n">iris</span><span class="o">$</span><span class="n">Species</span><span class="w"> </span><span class="o">%in%</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="s2">"setosa"</span><span class="p">,</span><span class="w"> </span><span class="s2">"versicolor"</span><span class="p">),</span><span class="w"> </span><span class="p">]</span><span class="w">
</span><span class="n">X_binary</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">iris_binary</span><span class="p">[,</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="m">4</span><span class="p">]</span><span class="w">
</span><span class="n">y_binary</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">as.factor</span><span class="p">(</span><span class="nf">as.character</span><span class="p">(</span><span class="n">iris_binary</span><span class="o">$</span><span class="n">Species</span><span class="p">))</span><span class="w">

</span><span class="c1"># Train/test split</span><span class="w">
</span><span class="n">train_idx</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">sample</span><span class="p">(</span><span class="nf">seq_len</span><span class="p">(</span><span class="n">nrow</span><span class="p">(</span><span class="n">X_binary</span><span class="p">)),</span><span class="w"> </span><span class="n">size</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.7</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">nrow</span><span class="p">(</span><span class="n">X_binary</span><span class="p">))</span><span class="w">
</span><span class="n">X_train</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">X_binary</span><span class="p">[</span><span class="n">train_idx</span><span class="p">,</span><span class="w"> </span><span class="p">]</span><span class="w">
</span><span class="n">y_train</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">y_binary</span><span class="p">[</span><span class="n">train_idx</span><span class="p">]</span><span class="w">
</span><span class="n">X_test</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">X_binary</span><span class="p">[</span><span class="o">-</span><span class="n">train_idx</span><span class="p">,</span><span class="w"> </span><span class="p">]</span><span class="w">
</span><span class="n">y_test</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">y_binary</span><span class="p">[</span><span class="o">-</span><span class="n">train_idx</span><span class="p">]</span><span class="w">

</span><span class="c1"># Initialize and fit model</span><span class="w">
</span><span class="c1"># Initialize model</span><span class="w">
</span><span class="n">mod</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">Model</span><span class="o">$</span><span class="n">new</span><span class="p">(</span><span class="n">my_ranger</span><span class="p">)</span><span class="w">

</span><span class="c1"># Fit on training data only</span><span class="w">
</span><span class="n">mod</span><span class="o">$</span><span class="n">fit</span><span class="p">(</span><span class="n">X_train</span><span class="p">,</span><span class="w"> </span><span class="n">y_train</span><span class="p">,</span><span class="w"> </span><span class="n">num.trees</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">150L</span><span class="p">)</span><span class="w">

</span><span class="c1"># Predict on test set</span><span class="w">
</span><span class="n">preds</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">mod</span><span class="o">$</span><span class="n">predict</span><span class="p">(</span><span class="n">X_test</span><span class="p">)</span><span class="w">

</span><span class="c1"># Evaluate</span><span class="w">
</span><span class="n">table</span><span class="p">(</span><span class="n">Predicted</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">preds</span><span class="p">,</span><span class="w"> </span><span class="n">True</span><span class="w"> </span><span class="o">=</span><span class="n">y_test</span><span class="p">)</span><span class="w">
</span><span class="n">mean</span><span class="p">(</span><span class="n">preds</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">y_test</span><span class="p">)</span><span class="w">  </span><span class="c1"># Accuracy</span><span class="w">



</span><span class="c1"># 5-fold cross-validation on training set</span><span class="w">
</span><span class="n">cv_scores</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">cross_val_score</span><span class="p">(</span><span class="w">
  </span><span class="n">mod</span><span class="p">,</span><span class="w">
  </span><span class="n">X_train</span><span class="p">,</span><span class="w">
  </span><span class="n">y_train</span><span class="p">,</span><span class="w">
  </span><span class="n">num.trees</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">150L</span><span class="p">,</span><span class="w">
  </span><span class="n">cv</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">5L</span><span class="w">
</span><span class="p">)</span><span class="w">

</span><span class="n">cv_scores</span><span class="w">
</span><span class="n">mean</span><span class="p">(</span><span class="n">cv_scores</span><span class="p">)</span><span class="w">  </span><span class="c1"># average CV accuracy</span><span class="w">

</span></code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>            True
Predicted    setosa versicolor
  setosa         15          0
  versicolor      0         15
</code></pre></div></div>

<p>1</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  |======================================================================| 100%
</code></pre></div></div>

<style>
.list-inline {list-style: none; margin:0; padding: 0}
.list-inline>li {display: inline-block}
.list-inline>li:not(:last-child)::after {content: "\00b7"; padding: 0 .5ex}
</style>

<p>&lt;ol class=list-inline&gt;&lt;li&gt;1&lt;/li&gt;&lt;li&gt;1&lt;/li&gt;&lt;li&gt;1&lt;/li&gt;&lt;li&gt;1&lt;/li&gt;&lt;li&gt;1&lt;/li&gt;&lt;/ol&gt;</p>

<p>1</p>

<h1 id="2---xgboost-example">2 - <code class="language-plaintext highlighter-rouge">xgboost</code> example</h1>

<div class="language-R highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">library</span><span class="p">(</span><span class="n">xgboost</span><span class="p">)</span><span class="w">

</span><span class="n">my_xgboost</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="p">,</span><span class="w"> </span><span class="n">...</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
  
  </span><span class="c1"># Convert to matrix safely</span><span class="w">
  </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="o">!</span><span class="nf">is.matrix</span><span class="p">(</span><span class="n">x</span><span class="p">))</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="n">x</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">as.matrix</span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w">
  </span><span class="p">}</span><span class="w">
  
  </span><span class="c1"># Handle factors</span><span class="w">
  </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">is.factor</span><span class="p">(</span><span class="n">y</span><span class="p">))</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="n">y</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">as.numeric</span><span class="p">(</span><span class="n">y</span><span class="p">)</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="m">1</span><span class="w">
  </span><span class="p">}</span><span class="w">
  
  </span><span class="n">fit</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">xgboost</span><span class="o">::</span><span class="n">xgboost</span><span class="p">(</span><span class="w">
    </span><span class="n">data</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">x</span><span class="p">,</span><span class="w">
    </span><span class="n">label</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">y</span><span class="p">,</span><span class="w">
    </span><span class="n">...</span><span class="w">
  </span><span class="p">)</span><span class="w">
  
  </span><span class="n">structure</span><span class="p">(</span><span class="nf">list</span><span class="p">(</span><span class="n">fit</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">fit</span><span class="p">),</span><span class="w"> </span><span class="n">class</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"my_xgboost"</span><span class="p">)</span><span class="w">
</span><span class="p">}</span><span class="w">

</span><span class="n">predict.my_xgboost</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">object</span><span class="p">,</span><span class="w"> </span><span class="n">newdata</span><span class="p">,</span><span class="w"> </span><span class="n">...</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
  
  </span><span class="c1"># Ensure matrix</span><span class="w">
  </span><span class="n">newdata</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">as.matrix</span><span class="p">(</span><span class="n">newdata</span><span class="p">)</span><span class="w">
  
  </span><span class="n">preds</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">predict</span><span class="p">(</span><span class="n">object</span><span class="o">$</span><span class="n">fit</span><span class="p">,</span><span class="w"> </span><span class="n">newdata</span><span class="p">)</span><span class="w">
  
  </span><span class="c1"># If binary classification → convert probs to class</span><span class="w">
  </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="o">!</span><span class="nf">is.null</span><span class="p">(</span><span class="n">object</span><span class="o">$</span><span class="n">fit</span><span class="o">$</span><span class="n">params</span><span class="o">$</span><span class="n">objective</span><span class="p">)</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w">
      </span><span class="n">grepl</span><span class="p">(</span><span class="s2">"binary"</span><span class="p">,</span><span class="w"> </span><span class="n">object</span><span class="o">$</span><span class="n">fit</span><span class="o">$</span><span class="n">params</span><span class="o">$</span><span class="n">objective</span><span class="p">))</span><span class="w"> </span><span class="p">{</span><span class="w">
    
    </span><span class="nf">return</span><span class="p">(</span><span class="n">ifelse</span><span class="p">(</span><span class="n">preds</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="m">0.5</span><span class="p">,</span><span class="w"> </span><span class="m">1</span><span class="p">,</span><span class="w"> </span><span class="m">0</span><span class="p">))</span><span class="w">
  </span><span class="p">}</span><span class="w">
  
  </span><span class="n">preds</span><span class="w">
</span><span class="p">}</span><span class="w">

</span><span class="n">predict.my_xgboost</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">object</span><span class="p">,</span><span class="w"> </span><span class="n">newdata</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">NULL</span><span class="p">,</span><span class="w"> </span><span class="n">newx</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">NULL</span><span class="p">,</span><span class="w"> </span><span class="n">...</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
  
  </span><span class="c1"># Accept both conventions</span><span class="w">
  </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="o">!</span><span class="nf">is.null</span><span class="p">(</span><span class="n">newx</span><span class="p">))</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="n">newdata</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">newx</span><span class="w">
  </span><span class="p">}</span><span class="w">
  
  </span><span class="n">newdata</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">as.matrix</span><span class="p">(</span><span class="n">newdata</span><span class="p">)</span><span class="w">
  
  </span><span class="n">preds</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">predict</span><span class="p">(</span><span class="n">object</span><span class="o">$</span><span class="n">fit</span><span class="p">,</span><span class="w"> </span><span class="n">newdata</span><span class="p">)</span><span class="w">
  
  </span><span class="c1"># Binary classification → class labels</span><span class="w">
  </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="o">!</span><span class="nf">is.null</span><span class="p">(</span><span class="n">object</span><span class="o">$</span><span class="n">fit</span><span class="o">$</span><span class="n">params</span><span class="o">$</span><span class="n">objective</span><span class="p">)</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w">
      </span><span class="n">grepl</span><span class="p">(</span><span class="s2">"binary"</span><span class="p">,</span><span class="w"> </span><span class="n">object</span><span class="o">$</span><span class="n">fit</span><span class="o">$</span><span class="n">params</span><span class="o">$</span><span class="n">objective</span><span class="p">))</span><span class="w"> </span><span class="p">{</span><span class="w">
    
    </span><span class="nf">return</span><span class="p">(</span><span class="n">ifelse</span><span class="p">(</span><span class="n">preds</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="m">0.5</span><span class="p">,</span><span class="w"> </span><span class="m">1</span><span class="p">,</span><span class="w"> </span><span class="m">0</span><span class="p">))</span><span class="w">
  </span><span class="p">}</span><span class="w">
  
  </span><span class="n">preds</span><span class="w">
</span><span class="p">}</span><span class="w">

</span><span class="n">print.my_xgboost</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">...</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="n">cat</span><span class="p">(</span><span class="s2">"my_xgboost model\n"</span><span class="p">)</span><span class="w">
  </span><span class="n">print</span><span class="p">(</span><span class="n">x</span><span class="o">$</span><span class="n">fit</span><span class="p">)</span><span class="w">
</span><span class="p">}</span><span class="w">


</span><span class="n">set.seed</span><span class="p">(</span><span class="m">123</span><span class="p">)</span><span class="w">  </span><span class="c1"># for reproducibility</span><span class="w">

</span><span class="c1"># Binary subset</span><span class="w">
</span><span class="n">iris_binary</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">iris</span><span class="p">[</span><span class="n">iris</span><span class="o">$</span><span class="n">Species</span><span class="w"> </span><span class="o">%in%</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="s2">"setosa"</span><span class="p">,</span><span class="w"> </span><span class="s2">"versicolor"</span><span class="p">),</span><span class="w"> </span><span class="p">]</span><span class="w">
</span><span class="n">X_binary</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">as.matrix</span><span class="p">(</span><span class="n">iris_binary</span><span class="p">[,</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="m">4</span><span class="p">])</span><span class="w">
</span><span class="n">y_binary</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">as.factor</span><span class="p">(</span><span class="nf">as.character</span><span class="p">(</span><span class="n">iris_binary</span><span class="o">$</span><span class="n">Species</span><span class="p">))</span><span class="w">

</span><span class="c1"># Split indices: 70% train, 30% test</span><span class="w">
</span><span class="n">train_idx</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">sample</span><span class="p">(</span><span class="nf">seq_len</span><span class="p">(</span><span class="n">nrow</span><span class="p">(</span><span class="n">X_binary</span><span class="p">)),</span><span class="w"> </span><span class="n">size</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.7</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">nrow</span><span class="p">(</span><span class="n">X_binary</span><span class="p">))</span><span class="w">
</span><span class="n">X_train</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">X_binary</span><span class="p">[</span><span class="n">train_idx</span><span class="p">,</span><span class="w"> </span><span class="p">]</span><span class="w">
</span><span class="n">y_train</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">y_binary</span><span class="p">[</span><span class="n">train_idx</span><span class="p">]</span><span class="w">
</span><span class="n">X_test</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">X_binary</span><span class="p">[</span><span class="o">-</span><span class="n">train_idx</span><span class="p">,</span><span class="w"> </span><span class="p">]</span><span class="w">
</span><span class="n">y_test</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">y_binary</span><span class="p">[</span><span class="o">-</span><span class="n">train_idx</span><span class="p">]</span><span class="w">

</span><span class="c1"># Initialize model</span><span class="w">
</span><span class="n">mod</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">Model</span><span class="o">$</span><span class="n">new</span><span class="p">(</span><span class="n">my_xgboost</span><span class="p">)</span><span class="w">

</span><span class="c1"># Fit on training data only</span><span class="w">
</span><span class="n">mod</span><span class="o">$</span><span class="n">fit</span><span class="p">(</span><span class="n">X_train</span><span class="p">,</span><span class="w"> </span><span class="n">y_train</span><span class="p">,</span><span class="w"> </span><span class="n">nrounds</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">50</span><span class="p">,</span><span class="w"> </span><span class="n">objective</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"binary:logistic"</span><span class="p">)</span><span class="w">

</span><span class="c1"># Predict on test set</span><span class="w">
</span><span class="n">preds</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">mod</span><span class="o">$</span><span class="n">predict</span><span class="p">(</span><span class="n">X_test</span><span class="p">)</span><span class="w">

</span><span class="c1"># Evaluate</span><span class="w">
</span><span class="n">table</span><span class="p">(</span><span class="n">Predicted</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">preds</span><span class="p">,</span><span class="w"> </span><span class="n">True</span><span class="w"> </span><span class="o">=</span><span class="n">y_test</span><span class="p">)</span><span class="w">
</span><span class="n">mean</span><span class="p">(</span><span class="n">preds</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">y_test</span><span class="p">)</span><span class="w">  </span><span class="c1"># Accuracy</span><span class="w">



</span><span class="c1"># 5-fold cross-validation on training set</span><span class="w">
</span><span class="n">cv_scores</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">cross_val_score</span><span class="p">(</span><span class="w">
  </span><span class="n">mod</span><span class="p">,</span><span class="w"> 
  </span><span class="n">X_train</span><span class="p">,</span><span class="w"> 
  </span><span class="n">y_train</span><span class="p">,</span><span class="w"> 
  </span><span class="n">nrounds</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">50</span><span class="p">,</span><span class="w"> 
  </span><span class="n">objective</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"binary:logistic"</span><span class="p">,</span><span class="w"> 
  </span><span class="n">cv</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">5L</span><span class="w">
</span><span class="p">)</span><span class="w">

</span><span class="n">cv_scores</span><span class="w">
</span><span class="n">mean</span><span class="p">(</span><span class="n">cv_scores</span><span class="p">)</span><span class="w">  </span><span class="c1"># average CV accuracy</span><span class="w">
</span></code></pre></div></div>

<p><img src="/images/2026-04-04/2026-04-04-image1.png" alt="image-title-here" class="img-responsive" /></p>]]></content><author><name></name></author><category term="R" /><summary type="html"><![CDATA[A new version of `unifiedml` is out; available on CRAN. `unifiedml` is an effort to offer a unified interface to R's machine learning models.]]></summary></entry><entry><title type="html">Techtonique dot net is down until further notice</title><link href="https://thierrymoudiki.github.io/blog/2026/04/01/r/python/techtonique/techtonique-dot-net-down" rel="alternate" type="text/html" title="Techtonique dot net is down until further notice" /><published>2026-04-01T00:00:00+00:00</published><updated>2026-04-01T00:00:00+00:00</updated><id>https://thierrymoudiki.github.io/blog/2026/04/01/r/python/techtonique/techtonique-dot-net-down</id><content type="html" xml:base="https://thierrymoudiki.github.io/blog/2026/04/01/r/python/techtonique/techtonique-dot-net-down"><![CDATA[<p><strong>IMPORTANT: The website <a href="https://www.techtonique.net">https://www.techtonique.net</a> is down until further notice.</strong></p>

<p><a href="https://www.techtonique.net">https://www.techtonique.net</a> contained an language-agnostic API for machine learning tasks (classification, regression, survival analysis, forecasting etc.).</p>

<p>As a result, do not buy the Gumroad tutorial then.</p>

<p>You can still use the packages <a href="https://github.com/Techtonique">https://github.com/Techtonique</a> locally.</p>

<p>PS: It’s not an April’s fool joke.</p>]]></content><author><name></name></author><category term="R" /><category term="Python" /><category term="Techtonique" /><summary type="html"><![CDATA[Techtonique dot net is down until further notice]]></summary></entry><entry><title type="html">Explaining Time-Series Forecasts with Sensitivity Analysis (ahead::dynrmf and external regressors)</title><link href="https://thierrymoudiki.github.io/blog/2026/03/29/r/sensi-dynrmf" rel="alternate" type="text/html" title="Explaining Time-Series Forecasts with Sensitivity Analysis (ahead::dynrmf and external regressors)" /><published>2026-03-29T00:00:00+00:00</published><updated>2026-03-29T00:00:00+00:00</updated><id>https://thierrymoudiki.github.io/blog/2026/03/29/r/sensi-dynrmf</id><content type="html" xml:base="https://thierrymoudiki.github.io/blog/2026/03/29/r/sensi-dynrmf"><![CDATA[<p>Following <a href="https://thierrymoudiki.github.io/blog/2026/03/08/r/exact-shapley-dynrmf">the post on exact Shapley values</a> for time series explainability, this post illustrates an example of how to use sensitivity analysis  to explain time series forecasts, based on the <code class="language-plaintext highlighter-rouge">ahead::dynrmf</code> model and external regressors. What is <strong>sensitivity analysis</strong> in this context? It’s about evaluating the impact of changes in the external regressors on the time series forecast.</p>

<p>The post uses the <a href="https://docs.techtonique.net/ahead/reference/dynrmf_sensi.html"><code class="language-plaintext highlighter-rouge">ahead::dynrmf_sensi</code></a> function to compute the sensitivities, and the <a href="https://docs.techtonique.net/ahead/reference/plot_dynrmf_sensitivity.html"><code class="language-plaintext highlighter-rouge">ahead::plot_dynrmf_sensitivity</code></a> function to plot the results.</p>

<p>First, install the package:</p>

<div class="language-R highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">devtools</span><span class="o">::</span><span class="n">install_github</span><span class="p">(</span><span class="s2">"Techtonique/ahead"</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p>Then, run the following code:</p>

<div class="language-R highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># devtools::install_github("Techtonique/ahead")</span><span class="w">
</span><span class="c1"># install.packages(c("fpp2", "e1071", "patchwork"))</span><span class="w">

</span><span class="n">library</span><span class="p">(</span><span class="n">ahead</span><span class="p">)</span><span class="w">
</span><span class="n">library</span><span class="p">(</span><span class="n">fpp2</span><span class="p">)</span><span class="w">
</span><span class="n">library</span><span class="p">(</span><span class="n">patchwork</span><span class="p">)</span><span class="w">
</span><span class="n">library</span><span class="p">(</span><span class="n">e1071</span><span class="p">)</span><span class="w">

</span><span class="cd">#' # Example 1: US Consumption vs Income</span><span class="w">
</span><span class="n">sensitivity_results_auto</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">ahead</span><span class="o">::</span><span class="n">dynrmf_sensi</span><span class="p">(</span><span class="w">
</span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">fpp2</span><span class="o">::</span><span class="n">uschange</span><span class="p">[,</span><span class="w"> </span><span class="s2">"Consumption"</span><span class="p">],</span><span class="w">
</span><span class="n">xreg</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">fpp2</span><span class="o">::</span><span class="n">uschange</span><span class="p">[,</span><span class="w"> </span><span class="s2">"Income"</span><span class="p">],</span><span class="w">
</span><span class="n">h</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">10</span><span class="w">
</span><span class="p">)</span><span class="w">

</span><span class="n">plot1</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">ahead</span><span class="o">::</span><span class="n">plot_dynrmf_sensitivity</span><span class="p">(</span><span class="n">sensitivity_results_auto</span><span class="p">,</span><span class="w"> 
                           </span><span class="n">title</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Sensitivity of Consumption to Income (Ridge)"</span><span class="p">,</span><span class="w">
                           </span><span class="n">y_label</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Effect (ΔConsumption / ΔIncome)"</span><span class="p">)</span><span class="w">

</span><span class="cd">#' # Example 1: US Consumption vs Income</span><span class="w">
</span><span class="n">sensitivity_results_auto_svm</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">ahead</span><span class="o">::</span><span class="n">dynrmf_sensi</span><span class="p">(</span><span class="w">
  </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">fpp2</span><span class="o">::</span><span class="n">uschange</span><span class="p">[,</span><span class="w"> </span><span class="s2">"Consumption"</span><span class="p">],</span><span class="w">
  </span><span class="n">xreg</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">fpp2</span><span class="o">::</span><span class="n">uschange</span><span class="p">[,</span><span class="w"> </span><span class="s2">"Income"</span><span class="p">],</span><span class="w">
  </span><span class="n">h</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">10</span><span class="p">,</span><span class="w"> 
  </span><span class="n">fit_func</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">e1071</span><span class="o">::</span><span class="n">svm</span><span class="w"> </span><span class="c1"># additional parameter passed to ahead::dynrmf</span><span class="w">
</span><span class="p">)</span><span class="w">

</span><span class="n">plot2</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">ahead</span><span class="o">::</span><span class="n">plot_dynrmf_sensitivity</span><span class="p">(</span><span class="n">sensitivity_results_auto_svm</span><span class="p">,</span><span class="w"> 
                                        </span><span class="n">title</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Sensitivity of Consumption to Income (SVM)"</span><span class="p">,</span><span class="w">
                                        </span><span class="n">y_label</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Effect (ΔConsumption / ΔIncome)"</span><span class="p">)</span><span class="w">

 
</span><span class="c1"># Example 2: TV Advertising vs Insurance Quotes</span><span class="w">
</span><span class="n">sensitivity_results_tv</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">ahead</span><span class="o">::</span><span class="n">dynrmf_sensi</span><span class="p">(</span><span class="w">
 </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">fpp2</span><span class="o">::</span><span class="n">insurance</span><span class="p">[,</span><span class="w"> </span><span class="s2">"Quotes"</span><span class="p">],</span><span class="w">
   </span><span class="n">xreg</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">fpp2</span><span class="o">::</span><span class="n">insurance</span><span class="p">[,</span><span class="w"> </span><span class="s2">"TV.advert"</span><span class="p">],</span><span class="w">
   </span><span class="n">h</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">8</span><span class="w">
 </span><span class="p">)</span><span class="w">

</span><span class="n">plot3</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">ahead</span><span class="o">::</span><span class="n">plot_dynrmf_sensitivity</span><span class="p">(</span><span class="n">sensitivity_results_tv</span><span class="p">,</span><span class="w">
                           </span><span class="n">title</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Sensitivity of Insurance Quotes to TV Advertising (Ridge)"</span><span class="p">,</span><span class="w">
                           </span><span class="n">y_label</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Effect (ΔQuotes / ΔTV.advert)"</span><span class="p">)</span><span class="w">

</span><span class="n">sensitivity_results_tv_svm</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">ahead</span><span class="o">::</span><span class="n">dynrmf_sensi</span><span class="p">(</span><span class="w">
  </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">fpp2</span><span class="o">::</span><span class="n">insurance</span><span class="p">[,</span><span class="w"> </span><span class="s2">"Quotes"</span><span class="p">],</span><span class="w">
  </span><span class="n">xreg</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">fpp2</span><span class="o">::</span><span class="n">insurance</span><span class="p">[,</span><span class="w"> </span><span class="s2">"TV.advert"</span><span class="p">],</span><span class="w">
  </span><span class="n">h</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">8</span><span class="p">,</span><span class="w"> 
  </span><span class="n">fit_func</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">e1071</span><span class="o">::</span><span class="n">svm</span><span class="w"> </span><span class="c1"># additional parameter passed to ahead::dynrmf</span><span class="w">
</span><span class="p">)</span><span class="w">

</span><span class="n">plot4</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">ahead</span><span class="o">::</span><span class="n">plot_dynrmf_sensitivity</span><span class="p">(</span><span class="n">sensitivity_results_tv_svm</span><span class="p">,</span><span class="w">
                                        </span><span class="n">title</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Sensitivity of Insurance Quotes to TV Advertising (SVM)"</span><span class="p">,</span><span class="w">
                                        </span><span class="n">y_label</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Effect (ΔQuotes / ΔTV.advert)"</span><span class="p">)</span><span class="w">

</span><span class="p">(</span><span class="n">plot1</span><span class="o">+</span><span class="n">plot2</span><span class="p">)</span><span class="w">

</span><span class="p">(</span><span class="n">plot3</span><span class="o">+</span><span class="n">plot4</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p><img src="/images/2026-03-29/2026-03-29-image1.png" alt="image-title-here" class="img-responsive" />
<img src="/images/2026-03-29/2026-03-29-image2.png" alt="image-title-here" class="img-responsive" /></p>]]></content><author><name></name></author><category term="R" /><summary type="html"><![CDATA[Explaining Time-Series Forecasts with Sensitivity Analysis (ahead::dynrmf and external regressors)]]></summary></entry><entry><title type="html">Python version of ‘Option pricing using time series models as market price of risk Pt.3’</title><link href="https://thierrymoudiki.github.io/blog/2026/03/22/python/python-Semi-parametric-MarketPriceofRisk-update" rel="alternate" type="text/html" title="Python version of ‘Option pricing using time series models as market price of risk Pt.3’" /><published>2026-03-22T00:00:00+00:00</published><updated>2026-03-22T00:00:00+00:00</updated><id>https://thierrymoudiki.github.io/blog/2026/03/22/python/python-Semi-parametric-MarketPriceofRisk-update</id><content type="html" xml:base="https://thierrymoudiki.github.io/blog/2026/03/22/python/python-Semi-parametric-MarketPriceofRisk-update"><![CDATA[<p>This post is the Python version of <a href="https://thierrymoudiki.github.io/blog/2026/03/16/r/Semi-parametric-MarketPriceofRisk-update">https://thierrymoudiki.github.io/blog/2026/03/16/r/Semi-parametric-MarketPriceofRisk-update</a>, and 3rd part of <a href="https://thierrymoudiki.github.io/blog/2025/12/07/r/forecasting/ARIMA-Pricing">https://thierrymoudiki.github.io/blog/2025/12/07/r/forecasting/ARIMA-Pricing</a> and <a href="https://thierrymoudiki.github.io/blog/2026/02/01/r/Semi-parametric-MarketPriceofRisk-Theta">https://thierrymoudiki.github.io/blog/2026/02/01/r/Semi-parametric-MarketPriceofRisk-Theta</a>. These posts showed how to use ARIMA and Theta as market price of risk, to then price options under a risk-neutral measure by resampling <em>martingale</em> innovations.</p>

<p>After thinking about it more, here’s a condensed version of the previous posts, with some formulas and  Python code examples.</p>

<h2 id="1-market-setting">1. Market setting</h2>

<p>Let</p>

<ul>
  <li>\(S_t\) = asset price</li>
  <li>\(r\) = risk-free rate</li>
  <li>\(T\) = maturity</li>
</ul>

<p>Define the <strong>discounted price process</strong></p>

\[D_t = e^{-rt} S_t\]

<p>Under the no-arbitrage principle (Fundamental Theorem of Asset Pricing), there exists a probability measure \(Q\) such that</p>

\[E_Q[D_t \mid \mathcal{F}_{t-1}] = D_{t-1}\]

<p>so \(D_t\) is a <strong>martingale</strong>.</p>

<h2 id="2-empirical-innovation-extraction">2. Empirical innovation extraction</h2>

<p>Given simulated or observed price paths \(S_t\), compute</p>

\[D_t = e^{-rt} S_t\]

<p>Define increments</p>

\[\Delta D_t = D_t - D_{t-1}\]

<p>Fit a time-series filter</p>

\[\Delta D_t = f(\Delta D_{t-1}, \ldots, \Delta D_{t-p}) + \varepsilon_t\]

<p>where</p>

\[E[\varepsilon_t] = 0\]

<h2 id="3-bootstrap-innovation-distribution">3. Bootstrap innovation distribution</h2>

<p>Let</p>

\[\{\varepsilon_1, \ldots, \varepsilon_T\}\]

<p>be the empirical innovations.</p>

<p>Generate bootstrap resamples</p>

\[\varepsilon_t^{(i)}, \quad i = 1, \ldots, N\]

<p>using stationary bootstrap. These sequences define the <strong>innovation law</strong>.</p>

<h2 id="4-martingale-reconstruction">4. Martingale reconstruction</h2>

<p>Define the discounted process recursively:</p>

\[D_0 = S_0\]

\[D_t = D_{t-1} + \varepsilon_t\]

<p>which implies</p>

\[D_t = S_0 + \sum_{i=1}^{t} \varepsilon_i\]

<p>Since</p>

\[E[\varepsilon_t] = 0\]

<p>we obtain</p>

\[E[D_t] = E[S_0 + \sum_{i=1}^{t} \varepsilon_i] = S_0 + \sum_{i=1}^{t} E[\varepsilon_i] = S_0\]

<h2 id="5-risk-neutral-price-process">5. Risk-neutral price process</h2>

<p>Recover the price process</p>

\[S_t = e^{rt} D_t\]

<p>Then</p>

\[E[e^{-rt} S_t] = S_0\]

<p>which satisfies the <strong>risk-neutral condition</strong>.</p>

<h2 id="6-monte-carlo-pricing">6. Monte Carlo pricing</h2>

<p>For payoff \(H(S_T)\), the derivative price is</p>

\[V_0 = e^{-rT} E_Q[H(S_T)]\]

<p>Estimated by Monte Carlo:</p>

\[V_0 \approx e^{-rT} \frac{1}{N}
\sum_{i=1}^{N} H(S_T^{(i)})\]

<p>Example (European call):</p>

\[C_0 = e^{-rT} E_Q[\max(S_T - K, 0)]\]

<p>Here’s the Python code for the whole process:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">"""
Option pricing using time series models as market price of risk (Pt. 3)
Python translation of: https://thierrymoudiki.github.io/blog/2026/03/16/r/Semi-parametric-MarketPriceofRisk-update

Methodology
-----------
1. Simulate asset price paths under the physical measure (GBM / Heston / SVJD).
2. Compute discounted prices  D_t = exp(-r·t) · S_t.
3. Fit an AR(1) or auto-ARIMA filter to ΔD_t; extract residuals.
4. Keep only "stationary" residuals (Ljung-Box p-value &gt; 0.05).
5. Centre and stationary-block-bootstrap resample → innovation law.
6. Reconstruct risk-neutral paths:  D_t = D_{t-1} + ε_t,  S_t = exp(r·t)·D_t.
7. Price European calls &amp; puts via Monte Carlo; compare with Black-Scholes.

Dependencies
------------
    pip install numpy pandas scipy statsmodels pmdarima matplotlib
"""</span>

<span class="kn">import</span> <span class="nn">warnings</span>
<span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="n">np</span>
<span class="kn">import</span> <span class="nn">pandas</span> <span class="k">as</span> <span class="n">pd</span>
<span class="kn">import</span> <span class="nn">matplotlib</span>
<span class="c1">#matplotlib.use("Agg")          # comment out if running interactively
</span><span class="kn">import</span> <span class="nn">matplotlib.pyplot</span> <span class="k">as</span> <span class="n">plt</span>
<span class="kn">from</span> <span class="nn">scipy</span> <span class="kn">import</span> <span class="n">stats</span>
<span class="kn">from</span> <span class="nn">scipy.stats</span> <span class="kn">import</span> <span class="n">norm</span>
<span class="kn">from</span> <span class="nn">statsmodels.stats.diagnostic</span> <span class="kn">import</span> <span class="n">acorr_ljungbox</span>

<span class="n">warnings</span><span class="p">.</span><span class="n">filterwarnings</span><span class="p">(</span><span class="s">"ignore"</span><span class="p">)</span>

<span class="c1"># ─────────────────────────────────────────────────────────────────────────────
# 0.  Global parameters
# ─────────────────────────────────────────────────────────────────────────────
</span><span class="n">RNG_SEED</span>       <span class="o">=</span> <span class="mi">123</span>
<span class="n">N_PATHS</span>        <span class="o">=</span> <span class="mi">250</span>        <span class="c1"># simulated paths (physical measure)
</span><span class="n">H</span>              <span class="o">=</span> <span class="mi">5</span>          <span class="c1"># horizon in years
</span><span class="n">FREQ</span>           <span class="o">=</span> <span class="mi">252</span>        <span class="c1"># trading days per year
</span><span class="n">N_STEPS</span>        <span class="o">=</span> <span class="n">H</span> <span class="o">*</span> <span class="n">FREQ</span>   <span class="c1"># total time steps
</span><span class="n">R</span>              <span class="o">=</span> <span class="mf">0.05</span>       <span class="c1"># risk-free rate
</span><span class="n">S0</span>             <span class="o">=</span> <span class="mf">100.0</span>      <span class="c1"># initial asset price
</span><span class="n">MU</span>             <span class="o">=</span> <span class="mf">0.08</span>       <span class="c1"># physical drift
</span><span class="n">SIGMA</span>          <span class="o">=</span> <span class="mf">0.04</span>       <span class="c1"># (base) volatility
</span><span class="n">N_SIMS</span>         <span class="o">=</span> <span class="mi">5_000</span>      <span class="c1"># Monte Carlo paths for pricing
</span>
<span class="n">CHOICE_PROCESS</span> <span class="o">=</span> <span class="s">"GBM"</span>      <span class="c1"># "GBM" | "Heston" | "SVJD"
</span><span class="n">CHOICE_FILTER</span>  <span class="o">=</span> <span class="s">"AR(1)"</span>    <span class="c1"># "AR(1)" | "auto.arima"
</span>                            <span class="c1"># NOTE: auto.arima is more accurate but slower
</span>
<span class="n">dt</span>    <span class="o">=</span> <span class="mf">1.0</span> <span class="o">/</span> <span class="n">FREQ</span>
<span class="n">t</span>     <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">arange</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">N_STEPS</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">*</span> <span class="n">dt</span>   <span class="c1"># shape (N_STEPS+1,)
</span><span class="n">rng</span>   <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">random</span><span class="p">.</span><span class="n">default_rng</span><span class="p">(</span><span class="n">RNG_SEED</span><span class="p">)</span>

<span class="c1"># ─────────────────────────────────────────────────────────────────────────────
# 1.  Simulation under the physical measure
# ─────────────────────────────────────────────────────────────────────────────
</span>
<span class="k">def</span> <span class="nf">sim_gbm</span><span class="p">(</span><span class="n">S0</span><span class="p">,</span> <span class="n">mu</span><span class="p">,</span> <span class="n">sigma</span><span class="p">,</span> <span class="n">t</span><span class="p">,</span> <span class="n">dt</span><span class="p">,</span> <span class="n">n_paths</span><span class="p">,</span> <span class="n">rng</span><span class="p">):</span>
    <span class="s">"""Geometric Brownian Motion — shape (T+1, n_paths)."""</span>
    <span class="n">Z</span>    <span class="o">=</span> <span class="n">rng</span><span class="p">.</span><span class="n">standard_normal</span><span class="p">((</span><span class="nb">len</span><span class="p">(</span><span class="n">t</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="n">n_paths</span><span class="p">))</span>
    <span class="n">lr</span>   <span class="o">=</span> <span class="p">(</span><span class="n">mu</span> <span class="o">-</span> <span class="mf">0.5</span> <span class="o">*</span> <span class="n">sigma</span><span class="o">**</span><span class="mi">2</span><span class="p">)</span> <span class="o">*</span> <span class="n">dt</span> <span class="o">+</span> <span class="n">sigma</span> <span class="o">*</span> <span class="n">np</span><span class="p">.</span><span class="n">sqrt</span><span class="p">(</span><span class="n">dt</span><span class="p">)</span> <span class="o">*</span> <span class="n">Z</span>
    <span class="n">logS</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">log</span><span class="p">(</span><span class="n">S0</span><span class="p">)</span> <span class="o">+</span> <span class="n">np</span><span class="p">.</span><span class="n">vstack</span><span class="p">([</span><span class="n">np</span><span class="p">.</span><span class="n">zeros</span><span class="p">(</span><span class="n">n_paths</span><span class="p">),</span> <span class="n">np</span><span class="p">.</span><span class="n">cumsum</span><span class="p">(</span><span class="n">lr</span><span class="p">,</span> <span class="n">axis</span><span class="o">=</span><span class="mi">0</span><span class="p">)])</span>
    <span class="k">return</span> <span class="n">np</span><span class="p">.</span><span class="n">exp</span><span class="p">(</span><span class="n">logS</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">sim_heston</span><span class="p">(</span><span class="n">S0</span><span class="p">,</span> <span class="n">mu</span><span class="p">,</span> <span class="n">kappa</span><span class="o">=</span><span class="mf">2.0</span><span class="p">,</span> <span class="n">theta</span><span class="o">=</span><span class="n">SIGMA</span><span class="o">**</span><span class="mi">2</span><span class="p">,</span> <span class="n">xi</span><span class="o">=</span><span class="mf">0.1</span><span class="p">,</span>
               <span class="n">rho</span><span class="o">=-</span><span class="mf">0.7</span><span class="p">,</span> <span class="n">v0</span><span class="o">=</span><span class="n">SIGMA</span><span class="o">**</span><span class="mi">2</span><span class="p">,</span>
               <span class="n">dt</span><span class="o">=</span><span class="n">dt</span><span class="p">,</span> <span class="n">n_steps</span><span class="o">=</span><span class="n">N_STEPS</span><span class="p">,</span> <span class="n">n_paths</span><span class="o">=</span><span class="n">N_PATHS</span><span class="p">,</span> <span class="n">rng</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
    <span class="s">"""Heston stochastic-volatility (no jumps), Euler-Maruyama."""</span>
    <span class="n">S</span><span class="p">,</span> <span class="n">v</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">zeros</span><span class="p">((</span><span class="n">n_steps</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">n_paths</span><span class="p">)),</span> <span class="n">np</span><span class="p">.</span><span class="n">zeros</span><span class="p">((</span><span class="n">n_steps</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">n_paths</span><span class="p">))</span>
    <span class="n">S</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">S0</span><span class="p">;</span>  <span class="n">v</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">v0</span>
    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">n_steps</span><span class="p">):</span>
        <span class="n">Z1</span>  <span class="o">=</span> <span class="n">rng</span><span class="p">.</span><span class="n">standard_normal</span><span class="p">(</span><span class="n">n_paths</span><span class="p">)</span>
        <span class="n">Z2</span>  <span class="o">=</span> <span class="n">rho</span> <span class="o">*</span> <span class="n">Z1</span> <span class="o">+</span> <span class="n">np</span><span class="p">.</span><span class="n">sqrt</span><span class="p">(</span><span class="mi">1</span> <span class="o">-</span> <span class="n">rho</span><span class="o">**</span><span class="mi">2</span><span class="p">)</span> <span class="o">*</span> <span class="n">rng</span><span class="p">.</span><span class="n">standard_normal</span><span class="p">(</span><span class="n">n_paths</span><span class="p">)</span>
        <span class="n">vp</span>  <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">maximum</span><span class="p">(</span><span class="n">v</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="mi">0</span><span class="p">)</span>
        <span class="n">v</span><span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">v</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">+</span> <span class="n">kappa</span> <span class="o">*</span> <span class="p">(</span><span class="n">theta</span> <span class="o">-</span> <span class="n">vp</span><span class="p">)</span> <span class="o">*</span> <span class="n">dt</span> <span class="o">+</span> <span class="n">xi</span> <span class="o">*</span> <span class="n">np</span><span class="p">.</span><span class="n">sqrt</span><span class="p">(</span><span class="n">vp</span> <span class="o">*</span> <span class="n">dt</span><span class="p">)</span> <span class="o">*</span> <span class="n">Z1</span>
        <span class="n">S</span><span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">S</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">*</span> <span class="n">np</span><span class="p">.</span><span class="n">exp</span><span class="p">((</span><span class="n">mu</span> <span class="o">-</span> <span class="mf">0.5</span> <span class="o">*</span> <span class="n">vp</span><span class="p">)</span> <span class="o">*</span> <span class="n">dt</span> <span class="o">+</span> <span class="n">np</span><span class="p">.</span><span class="n">sqrt</span><span class="p">(</span><span class="n">vp</span> <span class="o">*</span> <span class="n">dt</span><span class="p">)</span> <span class="o">*</span> <span class="n">Z2</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">S</span>


<span class="k">def</span> <span class="nf">sim_svjd</span><span class="p">(</span><span class="n">S0</span><span class="p">,</span> <span class="n">mu</span><span class="p">,</span> <span class="n">kappa</span><span class="o">=</span><span class="mf">2.0</span><span class="p">,</span> <span class="n">theta</span><span class="o">=</span><span class="n">SIGMA</span><span class="o">**</span><span class="mi">2</span><span class="p">,</span> <span class="n">xi</span><span class="o">=</span><span class="mf">0.1</span><span class="p">,</span>
             <span class="n">rho</span><span class="o">=-</span><span class="mf">0.7</span><span class="p">,</span> <span class="n">v0</span><span class="o">=</span><span class="n">SIGMA</span><span class="o">**</span><span class="mi">2</span><span class="p">,</span>
             <span class="n">lam</span><span class="o">=</span><span class="mf">0.1</span><span class="p">,</span> <span class="n">mu_J</span><span class="o">=</span><span class="mf">0.0</span><span class="p">,</span> <span class="n">sigma_J</span><span class="o">=</span><span class="mf">0.02</span><span class="p">,</span>
             <span class="n">dt</span><span class="o">=</span><span class="n">dt</span><span class="p">,</span> <span class="n">n_steps</span><span class="o">=</span><span class="n">N_STEPS</span><span class="p">,</span> <span class="n">n_paths</span><span class="o">=</span><span class="n">N_PATHS</span><span class="p">,</span> <span class="n">rng</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
    <span class="s">"""Bates / SVJD: Heston + compound-Poisson jumps in log-price."""</span>
    <span class="n">S</span><span class="p">,</span> <span class="n">v</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">zeros</span><span class="p">((</span><span class="n">n_steps</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">n_paths</span><span class="p">)),</span> <span class="n">np</span><span class="p">.</span><span class="n">zeros</span><span class="p">((</span><span class="n">n_steps</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">n_paths</span><span class="p">))</span>
    <span class="n">S</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">S0</span><span class="p">;</span>  <span class="n">v</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">v0</span>
    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">n_steps</span><span class="p">):</span>
        <span class="n">Z1</span>  <span class="o">=</span> <span class="n">rng</span><span class="p">.</span><span class="n">standard_normal</span><span class="p">(</span><span class="n">n_paths</span><span class="p">)</span>
        <span class="n">Z2</span>  <span class="o">=</span> <span class="n">rho</span> <span class="o">*</span> <span class="n">Z1</span> <span class="o">+</span> <span class="n">np</span><span class="p">.</span><span class="n">sqrt</span><span class="p">(</span><span class="mi">1</span> <span class="o">-</span> <span class="n">rho</span><span class="o">**</span><span class="mi">2</span><span class="p">)</span> <span class="o">*</span> <span class="n">rng</span><span class="p">.</span><span class="n">standard_normal</span><span class="p">(</span><span class="n">n_paths</span><span class="p">)</span>
        <span class="n">vp</span>  <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">maximum</span><span class="p">(</span><span class="n">v</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="mi">0</span><span class="p">)</span>
        <span class="n">v</span><span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">v</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">+</span> <span class="n">kappa</span> <span class="o">*</span> <span class="p">(</span><span class="n">theta</span> <span class="o">-</span> <span class="n">vp</span><span class="p">)</span> <span class="o">*</span> <span class="n">dt</span> <span class="o">+</span> <span class="n">xi</span> <span class="o">*</span> <span class="n">np</span><span class="p">.</span><span class="n">sqrt</span><span class="p">(</span><span class="n">vp</span> <span class="o">*</span> <span class="n">dt</span><span class="p">)</span> <span class="o">*</span> <span class="n">Z1</span>
        <span class="n">Nj</span>  <span class="o">=</span> <span class="n">rng</span><span class="p">.</span><span class="n">poisson</span><span class="p">(</span><span class="n">lam</span> <span class="o">*</span> <span class="n">dt</span><span class="p">,</span> <span class="n">n_paths</span><span class="p">)</span>
        <span class="n">J</span>   <span class="o">=</span> <span class="n">Nj</span> <span class="o">*</span> <span class="p">(</span><span class="n">mu_J</span> <span class="o">+</span> <span class="n">sigma_J</span> <span class="o">*</span> <span class="n">rng</span><span class="p">.</span><span class="n">standard_normal</span><span class="p">(</span><span class="n">n_paths</span><span class="p">))</span>
        <span class="n">drift</span> <span class="o">=</span> <span class="p">(</span><span class="n">mu</span> <span class="o">-</span> <span class="mf">0.5</span> <span class="o">*</span> <span class="n">vp</span> <span class="o">-</span> <span class="n">lam</span> <span class="o">*</span> <span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">exp</span><span class="p">(</span><span class="n">mu_J</span> <span class="o">+</span> <span class="mf">0.5</span> <span class="o">*</span> <span class="n">sigma_J</span><span class="o">**</span><span class="mi">2</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">))</span> <span class="o">*</span> <span class="n">dt</span>
        <span class="n">S</span><span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">S</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">*</span> <span class="n">np</span><span class="p">.</span><span class="n">exp</span><span class="p">(</span><span class="n">drift</span> <span class="o">+</span> <span class="n">np</span><span class="p">.</span><span class="n">sqrt</span><span class="p">(</span><span class="n">vp</span> <span class="o">*</span> <span class="n">dt</span><span class="p">)</span> <span class="o">*</span> <span class="n">Z2</span> <span class="o">+</span> <span class="n">J</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">S</span>


<span class="k">print</span><span class="p">(</span><span class="s">"Simulating price paths …"</span><span class="p">)</span>
<span class="n">sim_GBM</span>    <span class="o">=</span> <span class="n">sim_gbm</span><span class="p">(</span><span class="n">S0</span><span class="p">,</span> <span class="n">MU</span><span class="p">,</span> <span class="n">SIGMA</span><span class="p">,</span> <span class="n">t</span><span class="p">,</span> <span class="n">dt</span><span class="p">,</span> <span class="n">N_PATHS</span><span class="p">,</span> <span class="n">rng</span><span class="p">)</span>
<span class="n">sim_Heston</span> <span class="o">=</span> <span class="n">sim_heston</span><span class="p">(</span><span class="n">S0</span><span class="o">=</span><span class="n">S0</span><span class="p">,</span> <span class="n">mu</span><span class="o">=</span><span class="n">MU</span><span class="p">,</span> <span class="n">rng</span><span class="o">=</span><span class="n">rng</span><span class="p">)</span>
<span class="n">sim_SVJD</span>   <span class="o">=</span> <span class="n">sim_svjd</span><span class="p">(</span><span class="n">S0</span><span class="o">=</span><span class="n">S0</span><span class="p">,</span> <span class="n">mu</span><span class="o">=</span><span class="n">MU</span><span class="p">,</span> <span class="n">rng</span><span class="o">=</span><span class="n">rng</span><span class="p">)</span>

<span class="c1"># ─────────────────────────────────────────────────────────────────────────────
# 2.  Discounted prices  D_t = exp(-r·t)·S_t
# ─────────────────────────────────────────────────────────────────────────────
</span><span class="n">discount</span>    <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">exp</span><span class="p">(</span><span class="o">-</span><span class="n">R</span> <span class="o">*</span> <span class="n">t</span><span class="p">)[:,</span> <span class="bp">None</span><span class="p">]</span>           <span class="c1"># (T+1, 1)
</span><span class="n">disc_prices</span> <span class="o">=</span> <span class="p">{</span><span class="s">"GBM"</span>   <span class="p">:</span> <span class="n">discount</span> <span class="o">*</span> <span class="n">sim_GBM</span><span class="p">,</span>
               <span class="s">"Heston"</span><span class="p">:</span> <span class="n">discount</span> <span class="o">*</span> <span class="n">sim_Heston</span><span class="p">,</span>
               <span class="s">"SVJD"</span>  <span class="p">:</span> <span class="n">discount</span> <span class="o">*</span> <span class="n">sim_SVJD</span><span class="p">}[</span><span class="n">CHOICE_PROCESS</span><span class="p">]</span>

<span class="c1"># ─────────────────────────────────────────────────────────────────────────────
# 3.  First differences  ΔD_t
# ─────────────────────────────────────────────────────────────────────────────
</span><span class="n">diff_mart</span>  <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">diff</span><span class="p">(</span><span class="n">disc_prices</span><span class="p">,</span> <span class="n">axis</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span>       <span class="c1"># (T, N_PATHS)
</span><span class="n">n_dates</span>    <span class="o">=</span> <span class="n">diff_mart</span><span class="p">.</span><span class="n">shape</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="n">n_dates_1</span>  <span class="o">=</span> <span class="n">n_dates</span> <span class="o">-</span> <span class="mi">1</span>

<span class="c1"># ─────────────────────────────────────────────────────────────────────────────
# 4.  Time-series filter → residuals
# ─────────────────────────────────────────────────────────────────────────────
</span>
<span class="k">def</span> <span class="nf">fit_ar1</span><span class="p">(</span><span class="n">y</span><span class="p">):</span>
    <span class="s">"""Fit AR(1) without intercept; return residuals (length T-1)."""</span>
    <span class="n">X</span>    <span class="o">=</span> <span class="n">y</span><span class="p">[:</span><span class="o">-</span><span class="mi">1</span><span class="p">].</span><span class="n">reshape</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
    <span class="n">yy</span>   <span class="o">=</span> <span class="n">y</span><span class="p">[</span><span class="mi">1</span><span class="p">:]</span>
    <span class="n">coef</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">linalg</span><span class="p">.</span><span class="n">lstsq</span><span class="p">(</span><span class="n">X</span><span class="p">,</span> <span class="n">yy</span><span class="p">,</span> <span class="n">rcond</span><span class="o">=</span><span class="bp">None</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
    <span class="k">return</span> <span class="n">yy</span> <span class="o">-</span> <span class="n">X</span> <span class="o">@</span> <span class="n">coef</span>


<span class="k">def</span> <span class="nf">fit_auto_arima</span><span class="p">(</span><span class="n">y</span><span class="p">):</span>
    <span class="s">"""Fit automatic ARIMA (no intercept); return residuals."""</span>
    <span class="kn">import</span> <span class="nn">pmdarima</span> <span class="k">as</span> <span class="n">pm</span>
    <span class="n">model</span> <span class="o">=</span> <span class="n">pm</span><span class="p">.</span><span class="n">auto_arima</span><span class="p">(</span><span class="n">y</span><span class="p">,</span> <span class="n">with_intercept</span><span class="o">=</span><span class="bp">False</span><span class="p">,</span> <span class="n">seasonal</span><span class="o">=</span><span class="bp">False</span><span class="p">,</span>
                          <span class="n">information_criterion</span><span class="o">=</span><span class="s">"aic"</span><span class="p">,</span> <span class="n">stepwise</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span>
                          <span class="n">error_action</span><span class="o">=</span><span class="s">"ignore"</span><span class="p">,</span> <span class="n">suppress_warnings</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">model</span><span class="p">.</span><span class="n">resid</span><span class="p">()</span>


<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"Fitting </span><span class="si">{</span><span class="n">CHOICE_FILTER</span><span class="si">}</span><span class="s"> filter on </span><span class="si">{</span><span class="n">N_PATHS</span><span class="si">}</span><span class="s"> paths …"</span><span class="p">)</span>
<span class="n">resids_matrix</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">zeros</span><span class="p">((</span><span class="n">n_dates_1</span><span class="p">,</span> <span class="n">N_PATHS</span><span class="p">))</span>

<span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">N_PATHS</span><span class="p">):</span>
    <span class="k">if</span> <span class="n">j</span> <span class="o">%</span> <span class="mi">50</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
        <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"  path </span><span class="si">{</span><span class="n">j</span><span class="si">}</span><span class="s">/</span><span class="si">{</span><span class="n">N_PATHS</span><span class="si">}</span><span class="s">"</span><span class="p">,</span> <span class="n">end</span><span class="o">=</span><span class="s">"</span><span class="se">\r</span><span class="s">"</span><span class="p">)</span>
    <span class="n">y</span> <span class="o">=</span> <span class="n">diff_mart</span><span class="p">[:,</span> <span class="n">j</span><span class="p">]</span>
    <span class="k">if</span> <span class="n">CHOICE_FILTER</span> <span class="o">==</span> <span class="s">"AR(1)"</span><span class="p">:</span>
        <span class="n">resids_matrix</span><span class="p">[:,</span> <span class="n">j</span><span class="p">]</span> <span class="o">=</span> <span class="n">fit_ar1</span><span class="p">(</span><span class="n">y</span><span class="p">)</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="k">try</span><span class="p">:</span>
            <span class="n">r_auto</span> <span class="o">=</span> <span class="n">fit_auto_arima</span><span class="p">(</span><span class="n">y</span><span class="p">)</span>
            <span class="n">resids_matrix</span><span class="p">[</span><span class="o">-</span><span class="nb">len</span><span class="p">(</span><span class="n">r_auto</span><span class="p">):,</span> <span class="n">j</span><span class="p">]</span> <span class="o">=</span> <span class="n">r_auto</span>
        <span class="k">except</span> <span class="nb">Exception</span><span class="p">:</span>
            <span class="n">resids_matrix</span><span class="p">[:,</span> <span class="n">j</span><span class="p">]</span> <span class="o">=</span> <span class="n">fit_ar1</span><span class="p">(</span><span class="n">y</span><span class="p">)</span>   <span class="c1"># fallback
</span>
<span class="k">print</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">Filter done."</span><span class="p">)</span>

<span class="c1"># ─────────────────────────────────────────────────────────────────────────────
# 5.  Keep stationary columns  (Ljung-Box p-value &gt; 0.05)
# ─────────────────────────────────────────────────────────────────────────────
</span><span class="n">pvals</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">array</span><span class="p">([</span>
    <span class="n">acorr_ljungbox</span><span class="p">(</span><span class="n">resids_matrix</span><span class="p">[:,</span> <span class="n">j</span><span class="p">],</span> <span class="n">lags</span><span class="o">=</span><span class="p">[</span><span class="mi">10</span><span class="p">],</span> <span class="n">return_df</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
    <span class="p">[</span><span class="s">"lb_pvalue"</span><span class="p">].</span><span class="n">iloc</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
    <span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">N_PATHS</span><span class="p">)</span>
<span class="p">])</span>
<span class="n">stationary_cols</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">where</span><span class="p">(</span><span class="n">pvals</span> <span class="o">&gt;</span> <span class="mf">0.05</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"Stationary paths kept: </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">stationary_cols</span><span class="p">)</span><span class="si">}</span><span class="s">/</span><span class="si">{</span><span class="n">N_PATHS</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>

<span class="n">resids_stat</span>  <span class="o">=</span> <span class="n">resids_matrix</span><span class="p">[:,</span> <span class="n">stationary_cols</span><span class="p">]</span>
<span class="n">centered_res</span> <span class="o">=</span> <span class="n">resids_stat</span> <span class="o">-</span> <span class="n">resids_stat</span><span class="p">.</span><span class="n">mean</span><span class="p">(</span><span class="n">axis</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span>   <span class="c1"># centre each column
</span>
<span class="c1"># ─────────────────────────────────────────────────────────────────────────────
# 6.  Stationary block bootstrap
# ─────────────────────────────────────────────────────────────────────────────
</span>
<span class="k">def</span> <span class="nf">stationary_block_bootstrap</span><span class="p">(</span><span class="n">series</span><span class="p">,</span> <span class="n">n_out</span><span class="p">,</span> <span class="n">rng</span><span class="p">,</span> <span class="n">mean_block</span><span class="o">=</span><span class="mi">10</span><span class="p">):</span>
    <span class="s">"""
    Geometric block-length stationary bootstrap.
    Wraps around the series circularly; returns array of length n_out.
    """</span>
    <span class="n">T</span>   <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">series</span><span class="p">)</span>
    <span class="n">out</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">empty</span><span class="p">(</span><span class="n">n_out</span><span class="p">)</span>
    <span class="n">i</span>   <span class="o">=</span> <span class="mi">0</span>
    <span class="k">while</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">n_out</span><span class="p">:</span>
        <span class="n">start</span>  <span class="o">=</span> <span class="n">rng</span><span class="p">.</span><span class="n">integers</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">T</span><span class="p">)</span>
        <span class="n">length</span> <span class="o">=</span> <span class="n">rng</span><span class="p">.</span><span class="n">geometric</span><span class="p">(</span><span class="mf">1.0</span> <span class="o">/</span> <span class="n">mean_block</span><span class="p">)</span>
        <span class="n">block</span>  <span class="o">=</span> <span class="n">series</span><span class="p">[</span><span class="n">np</span><span class="p">.</span><span class="n">arange</span><span class="p">(</span><span class="n">start</span><span class="p">,</span> <span class="n">start</span> <span class="o">+</span> <span class="n">length</span><span class="p">)</span> <span class="o">%</span> <span class="n">T</span><span class="p">]</span>
        <span class="n">take</span>   <span class="o">=</span> <span class="nb">min</span><span class="p">(</span><span class="n">length</span><span class="p">,</span> <span class="n">n_out</span> <span class="o">-</span> <span class="n">i</span><span class="p">)</span>
        <span class="n">out</span><span class="p">[</span><span class="n">i</span><span class="p">:</span><span class="n">i</span> <span class="o">+</span> <span class="n">take</span><span class="p">]</span> <span class="o">=</span> <span class="n">block</span><span class="p">[:</span><span class="n">take</span><span class="p">]</span>
        <span class="n">i</span> <span class="o">+=</span> <span class="n">take</span>
    <span class="k">return</span> <span class="n">out</span>


<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"Bootstrapping </span><span class="si">{</span><span class="n">N_SIMS</span><span class="si">}</span><span class="s"> innovation sequences …"</span><span class="p">)</span>
<span class="n">M</span>          <span class="o">=</span> <span class="n">centered_res</span><span class="p">.</span><span class="n">shape</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
<span class="n">n_rows</span>     <span class="o">=</span> <span class="n">centered_res</span><span class="p">.</span><span class="n">shape</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="n">resampled</span>  <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">zeros</span><span class="p">((</span><span class="n">n_rows</span><span class="p">,</span> <span class="n">N_SIMS</span><span class="p">))</span>
<span class="n">rng_boot</span>   <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">random</span><span class="p">.</span><span class="n">default_rng</span><span class="p">(</span><span class="n">RNG_SEED</span> <span class="o">+</span> <span class="mi">7</span><span class="p">)</span>

<span class="k">for</span> <span class="n">s</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">N_SIMS</span><span class="p">):</span>
    <span class="n">resampled</span><span class="p">[:,</span> <span class="n">s</span><span class="p">]</span> <span class="o">=</span> <span class="n">stationary_block_bootstrap</span><span class="p">(</span>
        <span class="n">centered_res</span><span class="p">[:,</span> <span class="n">s</span> <span class="o">%</span> <span class="n">M</span><span class="p">],</span> <span class="n">n_rows</span><span class="p">,</span> <span class="n">rng_boot</span>
    <span class="p">)</span>

<span class="c1"># ─────────────────────────────────────────────────────────────────────────────
# 7.  Reconstruct discounted paths → risk-neutral prices
#      D_0 = S_0,  D_t = D_0 + Σ_{i=1}^t ε_i,  S_t^Q = exp(r·t)·D_t
# ─────────────────────────────────────────────────────────────────────────────
</span><span class="n">D0</span> <span class="o">=</span> <span class="n">S0</span>
<span class="n">discounted_paths</span>    <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">vstack</span><span class="p">([</span><span class="n">np</span><span class="p">.</span><span class="n">full</span><span class="p">(</span><span class="n">N_SIMS</span><span class="p">,</span> <span class="n">D0</span><span class="p">),</span>
                                 <span class="n">D0</span> <span class="o">+</span> <span class="n">np</span><span class="p">.</span><span class="n">cumsum</span><span class="p">(</span><span class="n">resampled</span><span class="p">,</span> <span class="n">axis</span><span class="o">=</span><span class="mi">0</span><span class="p">)])</span>
<span class="n">t_full</span>              <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">arange</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">n_rows</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">*</span> <span class="n">dt</span>     <span class="c1"># (T+1,)
</span><span class="n">risk_neutral_prices</span> <span class="o">=</span> <span class="n">discounted_paths</span> <span class="o">*</span> <span class="n">np</span><span class="p">.</span><span class="n">exp</span><span class="p">(</span><span class="n">R</span> <span class="o">*</span> <span class="n">t_full</span><span class="p">)[:,</span> <span class="bp">None</span><span class="p">]</span>

<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"Risk-neutral price matrix shape: </span><span class="si">{</span><span class="n">risk_neutral_prices</span><span class="p">.</span><span class="n">shape</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>

<span class="c1"># ─────────────────────────────────────────────────────────────────────────────
# 8.  Diagnostics
# ─────────────────────────────────────────────────────────────────────────────
</span><span class="k">print</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">"</span> <span class="o">+</span> <span class="s">"="</span><span class="o">*</span><span class="mi">60</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s">"I. Basic diagnostics"</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s">"="</span><span class="o">*</span><span class="mi">60</span><span class="p">)</span>

<span class="n">n_negative</span> <span class="o">=</span> <span class="p">(</span><span class="n">risk_neutral_prices</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">).</span><span class="nb">sum</span><span class="p">()</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"Negative prices : </span><span class="si">{</span><span class="n">n_negative</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
<span class="k">assert</span> <span class="n">n_negative</span> <span class="o">==</span> <span class="mi">0</span><span class="p">,</span> <span class="s">"Negative prices detected — check parameters."</span>

<span class="n">S_T</span> <span class="o">=</span> <span class="n">risk_neutral_prices</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="p">:]</span>
<span class="k">print</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">Terminal price S_T summary:"</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">pd</span><span class="p">.</span><span class="n">Series</span><span class="p">(</span><span class="n">S_T</span><span class="p">).</span><span class="n">describe</span><span class="p">().</span><span class="nb">round</span><span class="p">(</span><span class="mi">4</span><span class="p">).</span><span class="n">to_string</span><span class="p">())</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"Skewness        : </span><span class="si">{</span><span class="n">stats</span><span class="p">.</span><span class="n">skew</span><span class="p">(</span><span class="n">S_T</span><span class="p">)</span><span class="si">:</span><span class="p">.</span><span class="mi">4</span><span class="n">f</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"Excess kurtosis : </span><span class="si">{</span><span class="n">stats</span><span class="p">.</span><span class="n">kurtosis</span><span class="p">(</span><span class="n">S_T</span><span class="p">)</span><span class="si">:</span><span class="p">.</span><span class="mi">4</span><span class="n">f</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>

<span class="k">print</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">"</span> <span class="o">+</span> <span class="s">"="</span><span class="o">*</span><span class="mi">60</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s">"II. Martingale check"</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s">"="</span><span class="o">*</span><span class="mi">60</span><span class="p">)</span>

<span class="n">T_years</span>    <span class="o">=</span> <span class="nb">float</span><span class="p">(</span><span class="n">H</span><span class="p">)</span>
<span class="n">discount_T</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">exp</span><span class="p">(</span><span class="o">-</span><span class="n">R</span> <span class="o">*</span> <span class="n">T_years</span><span class="p">)</span>
<span class="n">mc_price</span>   <span class="o">=</span> <span class="n">discount_T</span> <span class="o">*</span> <span class="n">S_T</span><span class="p">.</span><span class="n">mean</span><span class="p">()</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"S_0                    : </span><span class="si">{</span><span class="n">S0</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"E[exp(-rT)·S_T]        : </span><span class="si">{</span><span class="n">mc_price</span><span class="si">:</span><span class="p">.</span><span class="mi">4</span><span class="n">f</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"|error|                : </span><span class="si">{</span><span class="nb">abs</span><span class="p">(</span><span class="n">mc_price</span> <span class="o">-</span> <span class="n">S0</span><span class="p">)</span><span class="si">:</span><span class="p">.</span><span class="mi">4</span><span class="n">f</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>

<span class="n">disc_check</span> <span class="o">=</span> <span class="n">risk_neutral_prices</span> <span class="o">*</span> <span class="n">np</span><span class="p">.</span><span class="n">exp</span><span class="p">(</span><span class="o">-</span><span class="n">R</span> <span class="o">*</span> <span class="n">t_full</span><span class="p">)[:,</span> <span class="bp">None</span><span class="p">]</span>
<span class="n">mean_disc</span>  <span class="o">=</span> <span class="n">disc_check</span><span class="p">.</span><span class="n">mean</span><span class="p">(</span><span class="n">axis</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">Mean discounted price – first 6 time points:"</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="nb">round</span><span class="p">(</span><span class="n">mean_disc</span><span class="p">[:</span><span class="mi">6</span><span class="p">],</span> <span class="mi">4</span><span class="p">))</span>
<span class="k">print</span><span class="p">(</span><span class="s">"Mean discounted price – last 6 time points:"</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="nb">round</span><span class="p">(</span><span class="n">mean_disc</span><span class="p">[</span><span class="o">-</span><span class="mi">6</span><span class="p">:],</span> <span class="mi">4</span><span class="p">))</span>

<span class="n">tstat</span><span class="p">,</span> <span class="n">pval</span> <span class="o">=</span> <span class="n">stats</span><span class="p">.</span><span class="n">ttest_1samp</span><span class="p">(</span><span class="n">discount_T</span> <span class="o">*</span> <span class="n">S_T</span> <span class="o">-</span> <span class="n">S0</span><span class="p">,</span> <span class="n">popmean</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"</span><span class="se">\n</span><span class="s">t-test H0: E[exp(-rT)·S_T] = S0  →  t=</span><span class="si">{</span><span class="n">tstat</span><span class="si">:</span><span class="p">.</span><span class="mi">4</span><span class="n">f</span><span class="si">}</span><span class="s">, p=</span><span class="si">{</span><span class="n">pval</span><span class="si">:</span><span class="p">.</span><span class="mi">4</span><span class="n">f</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>

<span class="k">print</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">"</span> <span class="o">+</span> <span class="s">"="</span><span class="o">*</span><span class="mi">60</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s">"III. Log-return distribution"</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s">"="</span><span class="o">*</span><span class="mi">60</span><span class="p">)</span>

<span class="n">log_ret</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">diff</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">log</span><span class="p">(</span><span class="n">risk_neutral_prices</span><span class="p">),</span> <span class="n">axis</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span>
<span class="n">lr_T</span>    <span class="o">=</span> <span class="n">log_ret</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="p">:]</span>

<span class="n">sw_stat</span><span class="p">,</span> <span class="n">sw_p</span> <span class="o">=</span> <span class="n">stats</span><span class="p">.</span><span class="n">shapiro</span><span class="p">(</span><span class="n">lr_T</span><span class="p">[:</span><span class="mi">5000</span><span class="p">])</span>
<span class="n">ks_stat</span><span class="p">,</span> <span class="n">ks_p</span> <span class="o">=</span> <span class="n">stats</span><span class="p">.</span><span class="n">kstest</span><span class="p">(</span><span class="n">lr_T</span><span class="p">,</span> <span class="s">"norm"</span><span class="p">,</span> <span class="n">args</span><span class="o">=</span><span class="p">(</span><span class="n">lr_T</span><span class="p">.</span><span class="n">mean</span><span class="p">(),</span> <span class="n">lr_T</span><span class="p">.</span><span class="n">std</span><span class="p">()))</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"Shapiro-Wilk  : W=</span><span class="si">{</span><span class="n">sw_stat</span><span class="si">:</span><span class="p">.</span><span class="mi">6</span><span class="n">f</span><span class="si">}</span><span class="s">, p=</span><span class="si">{</span><span class="n">sw_p</span><span class="si">:</span><span class="p">.</span><span class="mi">4</span><span class="n">e</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"KS vs Normal  : D=</span><span class="si">{</span><span class="n">ks_stat</span><span class="si">:</span><span class="p">.</span><span class="mi">6</span><span class="n">f</span><span class="si">}</span><span class="s">, p=</span><span class="si">{</span><span class="n">ks_p</span><span class="si">:</span><span class="p">.</span><span class="mi">4</span><span class="n">e</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>

<span class="n">mean_lr</span>     <span class="o">=</span> <span class="n">lr_T</span><span class="p">.</span><span class="n">mean</span><span class="p">()</span>
<span class="n">theoretical</span> <span class="o">=</span> <span class="p">(</span><span class="n">R</span> <span class="o">-</span> <span class="mf">0.5</span> <span class="o">*</span> <span class="n">SIGMA</span><span class="o">**</span><span class="mi">2</span><span class="p">)</span> <span class="o">*</span> <span class="n">dt</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"</span><span class="se">\n</span><span class="s">Mean terminal log-return  : </span><span class="si">{</span><span class="n">mean_lr</span><span class="si">:</span><span class="p">.</span><span class="mi">6</span><span class="n">f</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"Theoretical (r-σ²/2)·dt  : </span><span class="si">{</span><span class="n">theoretical</span><span class="si">:</span><span class="p">.</span><span class="mi">6</span><span class="n">f</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>

<span class="n">emp_var</span>  <span class="o">=</span> <span class="nb">float</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">var</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">log</span><span class="p">(</span><span class="n">S_T</span><span class="p">)))</span>
<span class="n">theo_var</span> <span class="o">=</span> <span class="n">SIGMA</span><span class="o">**</span><span class="mi">2</span> <span class="o">*</span> <span class="n">T_years</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"</span><span class="se">\n</span><span class="s">Var(log S_T) empirical  : </span><span class="si">{</span><span class="n">emp_var</span><span class="si">:</span><span class="p">.</span><span class="mi">4</span><span class="n">f</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"Var(log S_T) GBM theory : </span><span class="si">{</span><span class="n">theo_var</span><span class="si">:</span><span class="p">.</span><span class="mi">4</span><span class="n">f</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"Ratio                   : </span><span class="si">{</span><span class="n">emp_var</span><span class="o">/</span><span class="n">theo_var</span><span class="si">:</span><span class="p">.</span><span class="mi">4</span><span class="n">f</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>

<span class="c1"># ─────────────────────────────────────────────────────────────────────────────
# 9.  European option pricing
# ─────────────────────────────────────────────────────────────────────────────
</span><span class="k">print</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">"</span> <span class="o">+</span> <span class="s">"="</span><span class="o">*</span><span class="mi">60</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s">"IV. European option prices: MC vs Black-Scholes"</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s">"="</span><span class="o">*</span><span class="mi">60</span><span class="p">)</span>

<span class="n">strikes</span> <span class="o">=</span> <span class="p">[</span><span class="mi">80</span><span class="p">,</span> <span class="mi">90</span><span class="p">,</span> <span class="mi">95</span><span class="p">,</span> <span class="mi">100</span><span class="p">,</span> <span class="mi">105</span><span class="p">,</span> <span class="mi">110</span><span class="p">,</span> <span class="mi">120</span><span class="p">]</span>

<span class="n">mc_call</span> <span class="o">=</span> <span class="p">[</span><span class="n">np</span><span class="p">.</span><span class="n">exp</span><span class="p">(</span><span class="o">-</span><span class="n">R</span> <span class="o">*</span> <span class="n">T_years</span><span class="p">)</span> <span class="o">*</span> <span class="n">np</span><span class="p">.</span><span class="n">mean</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">maximum</span><span class="p">(</span><span class="n">S_T</span> <span class="o">-</span> <span class="n">K</span><span class="p">,</span> <span class="mi">0</span><span class="p">))</span> <span class="k">for</span> <span class="n">K</span> <span class="ow">in</span> <span class="n">strikes</span><span class="p">]</span>
<span class="n">mc_put</span>  <span class="o">=</span> <span class="p">[</span><span class="n">np</span><span class="p">.</span><span class="n">exp</span><span class="p">(</span><span class="o">-</span><span class="n">R</span> <span class="o">*</span> <span class="n">T_years</span><span class="p">)</span> <span class="o">*</span> <span class="n">np</span><span class="p">.</span><span class="n">mean</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">maximum</span><span class="p">(</span><span class="n">K</span> <span class="o">-</span> <span class="n">S_T</span><span class="p">,</span> <span class="mi">0</span><span class="p">))</span> <span class="k">for</span> <span class="n">K</span> <span class="ow">in</span> <span class="n">strikes</span><span class="p">]</span>


<span class="k">def</span> <span class="nf">bs_call</span><span class="p">(</span><span class="n">S</span><span class="p">,</span> <span class="n">K</span><span class="p">,</span> <span class="n">r</span><span class="p">,</span> <span class="n">sigma</span><span class="p">,</span> <span class="n">T</span><span class="p">):</span>
    <span class="n">d1</span> <span class="o">=</span> <span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">log</span><span class="p">(</span><span class="n">S</span><span class="o">/</span><span class="n">K</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="n">r</span> <span class="o">+</span> <span class="mf">0.5</span><span class="o">*</span><span class="n">sigma</span><span class="o">**</span><span class="mi">2</span><span class="p">)</span><span class="o">*</span><span class="n">T</span><span class="p">)</span> <span class="o">/</span> <span class="p">(</span><span class="n">sigma</span><span class="o">*</span><span class="n">np</span><span class="p">.</span><span class="n">sqrt</span><span class="p">(</span><span class="n">T</span><span class="p">))</span>
    <span class="n">d2</span> <span class="o">=</span> <span class="n">d1</span> <span class="o">-</span> <span class="n">sigma</span><span class="o">*</span><span class="n">np</span><span class="p">.</span><span class="n">sqrt</span><span class="p">(</span><span class="n">T</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">S</span> <span class="o">*</span> <span class="n">norm</span><span class="p">.</span><span class="n">cdf</span><span class="p">(</span><span class="n">d1</span><span class="p">)</span> <span class="o">-</span> <span class="n">K</span> <span class="o">*</span> <span class="n">np</span><span class="p">.</span><span class="n">exp</span><span class="p">(</span><span class="o">-</span><span class="n">r</span><span class="o">*</span><span class="n">T</span><span class="p">)</span> <span class="o">*</span> <span class="n">norm</span><span class="p">.</span><span class="n">cdf</span><span class="p">(</span><span class="n">d2</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">bs_put</span><span class="p">(</span><span class="n">S</span><span class="p">,</span> <span class="n">K</span><span class="p">,</span> <span class="n">r</span><span class="p">,</span> <span class="n">sigma</span><span class="p">,</span> <span class="n">T</span><span class="p">):</span>
    <span class="n">d1</span> <span class="o">=</span> <span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">log</span><span class="p">(</span><span class="n">S</span><span class="o">/</span><span class="n">K</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="n">r</span> <span class="o">+</span> <span class="mf">0.5</span><span class="o">*</span><span class="n">sigma</span><span class="o">**</span><span class="mi">2</span><span class="p">)</span><span class="o">*</span><span class="n">T</span><span class="p">)</span> <span class="o">/</span> <span class="p">(</span><span class="n">sigma</span><span class="o">*</span><span class="n">np</span><span class="p">.</span><span class="n">sqrt</span><span class="p">(</span><span class="n">T</span><span class="p">))</span>
    <span class="n">d2</span> <span class="o">=</span> <span class="n">d1</span> <span class="o">-</span> <span class="n">sigma</span><span class="o">*</span><span class="n">np</span><span class="p">.</span><span class="n">sqrt</span><span class="p">(</span><span class="n">T</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">K</span> <span class="o">*</span> <span class="n">np</span><span class="p">.</span><span class="n">exp</span><span class="p">(</span><span class="o">-</span><span class="n">r</span><span class="o">*</span><span class="n">T</span><span class="p">)</span> <span class="o">*</span> <span class="n">norm</span><span class="p">.</span><span class="n">cdf</span><span class="p">(</span><span class="o">-</span><span class="n">d2</span><span class="p">)</span> <span class="o">-</span> <span class="n">S</span> <span class="o">*</span> <span class="n">norm</span><span class="p">.</span><span class="n">cdf</span><span class="p">(</span><span class="o">-</span><span class="n">d1</span><span class="p">)</span>


<span class="n">bsc</span> <span class="o">=</span> <span class="p">[</span><span class="n">bs_call</span><span class="p">(</span><span class="n">S0</span><span class="p">,</span> <span class="n">K</span><span class="p">,</span> <span class="n">R</span><span class="p">,</span> <span class="n">SIGMA</span><span class="p">,</span> <span class="n">T_years</span><span class="p">)</span> <span class="k">for</span> <span class="n">K</span> <span class="ow">in</span> <span class="n">strikes</span><span class="p">]</span>
<span class="n">bsp</span> <span class="o">=</span> <span class="p">[</span><span class="n">bs_put</span><span class="p">(</span><span class="n">S0</span><span class="p">,</span>  <span class="n">K</span><span class="p">,</span> <span class="n">R</span><span class="p">,</span> <span class="n">SIGMA</span><span class="p">,</span> <span class="n">T_years</span><span class="p">)</span> <span class="k">for</span> <span class="n">K</span> <span class="ow">in</span> <span class="n">strikes</span><span class="p">]</span>

<span class="n">pcp_mc</span>  <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">array</span><span class="p">(</span><span class="n">mc_call</span><span class="p">)</span> <span class="o">-</span> <span class="n">np</span><span class="p">.</span><span class="n">array</span><span class="p">(</span><span class="n">mc_put</span><span class="p">)</span>
<span class="n">pcp_th</span>  <span class="o">=</span> <span class="n">S0</span> <span class="o">-</span> <span class="n">np</span><span class="p">.</span><span class="n">array</span><span class="p">(</span><span class="n">strikes</span><span class="p">)</span> <span class="o">*</span> <span class="n">np</span><span class="p">.</span><span class="n">exp</span><span class="p">(</span><span class="o">-</span><span class="n">R</span> <span class="o">*</span> <span class="n">T_years</span><span class="p">)</span>
<span class="n">pcp_err</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="nb">abs</span><span class="p">(</span><span class="n">pcp_mc</span> <span class="o">-</span> <span class="n">pcp_th</span><span class="p">)</span>

<span class="n">results</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="n">DataFrame</span><span class="p">({</span>
    <span class="s">"K"</span>        <span class="p">:</span> <span class="n">strikes</span><span class="p">,</span>
    <span class="s">"BS_call"</span>  <span class="p">:</span> <span class="n">np</span><span class="p">.</span><span class="nb">round</span><span class="p">(</span><span class="n">bsc</span><span class="p">,</span> <span class="mi">4</span><span class="p">),</span>
    <span class="s">"MC_call"</span>  <span class="p">:</span> <span class="n">np</span><span class="p">.</span><span class="nb">round</span><span class="p">(</span><span class="n">mc_call</span><span class="p">,</span> <span class="mi">4</span><span class="p">),</span>
    <span class="s">"err_call"</span> <span class="p">:</span> <span class="n">np</span><span class="p">.</span><span class="nb">round</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="nb">abs</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">array</span><span class="p">(</span><span class="n">mc_call</span><span class="p">)</span> <span class="o">-</span> <span class="n">np</span><span class="p">.</span><span class="n">array</span><span class="p">(</span><span class="n">bsc</span><span class="p">)),</span> <span class="mi">4</span><span class="p">),</span>
    <span class="s">"BS_put"</span>   <span class="p">:</span> <span class="n">np</span><span class="p">.</span><span class="nb">round</span><span class="p">(</span><span class="n">bsp</span><span class="p">,</span> <span class="mi">4</span><span class="p">),</span>
    <span class="s">"MC_put"</span>   <span class="p">:</span> <span class="n">np</span><span class="p">.</span><span class="nb">round</span><span class="p">(</span><span class="n">mc_put</span><span class="p">,</span> <span class="mi">4</span><span class="p">),</span>
    <span class="s">"err_put"</span>  <span class="p">:</span> <span class="n">np</span><span class="p">.</span><span class="nb">round</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="nb">abs</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">array</span><span class="p">(</span><span class="n">mc_put</span><span class="p">)</span> <span class="o">-</span> <span class="n">np</span><span class="p">.</span><span class="n">array</span><span class="p">(</span><span class="n">bsp</span><span class="p">)),</span> <span class="mi">4</span><span class="p">),</span>
    <span class="s">"PCP_error"</span><span class="p">:</span> <span class="n">np</span><span class="p">.</span><span class="nb">round</span><span class="p">(</span><span class="n">pcp_err</span><span class="p">,</span> <span class="mi">4</span><span class="p">),</span>
<span class="p">})</span>
<span class="k">print</span><span class="p">(</span><span class="n">results</span><span class="p">.</span><span class="n">to_string</span><span class="p">(</span><span class="n">index</span><span class="o">=</span><span class="bp">False</span><span class="p">))</span>

<span class="c1"># ─────────────────────────────────────────────────────────────────────────────
# 10. Plots
# ─────────────────────────────────────────────────────────────────────────────
</span><span class="n">plt</span><span class="p">.</span><span class="n">close</span><span class="p">(</span><span class="s">"all"</span><span class="p">)</span>   <span class="c1"># ← add this line
</span><span class="n">fig</span><span class="p">,</span> <span class="n">axes</span> <span class="o">=</span> <span class="n">plt</span><span class="p">.</span><span class="n">subplots</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="n">figsize</span><span class="o">=</span><span class="p">(</span><span class="mi">18</span><span class="p">,</span> <span class="mi">5</span><span class="p">))</span>
<span class="n">fig</span><span class="p">.</span><span class="n">suptitle</span><span class="p">(</span>
    <span class="sa">f</span><span class="s">"Semi-parametric option pricing  |  process=</span><span class="si">{</span><span class="n">CHOICE_PROCESS</span><span class="si">}</span><span class="s">, filter=</span><span class="si">{</span><span class="n">CHOICE_FILTER</span><span class="si">}</span><span class="s">"</span><span class="p">,</span>
    <span class="n">fontsize</span><span class="o">=</span><span class="mi">13</span><span class="p">,</span> <span class="n">fontweight</span><span class="o">=</span><span class="s">"bold"</span>
<span class="p">)</span>

<span class="c1"># Fan chart of risk-neutral paths
</span><span class="n">ax</span>     <span class="o">=</span> <span class="n">axes</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="n">t_plot</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">linspace</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">T_years</span><span class="p">,</span> <span class="n">risk_neutral_prices</span><span class="p">.</span><span class="n">shape</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
<span class="n">q5</span><span class="p">,</span> <span class="n">q50</span><span class="p">,</span> <span class="n">q95</span> <span class="o">=</span> <span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">percentile</span><span class="p">(</span><span class="n">risk_neutral_prices</span><span class="p">,</span> <span class="n">q</span><span class="p">,</span> <span class="n">axis</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span> <span class="k">for</span> <span class="n">q</span> <span class="ow">in</span> <span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="mi">50</span><span class="p">,</span> <span class="mi">95</span><span class="p">))</span>
<span class="n">ax</span><span class="p">.</span><span class="n">fill_between</span><span class="p">(</span><span class="n">t_plot</span><span class="p">,</span> <span class="n">q5</span><span class="p">,</span> <span class="n">q95</span><span class="p">,</span> <span class="n">alpha</span><span class="o">=</span><span class="mf">0.30</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s">"steelblue"</span><span class="p">,</span> <span class="n">label</span><span class="o">=</span><span class="s">"5–95 %"</span><span class="p">)</span>
<span class="n">ax</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">t_plot</span><span class="p">,</span> <span class="n">q50</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s">"steelblue"</span><span class="p">,</span> <span class="n">lw</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span> <span class="n">label</span><span class="o">=</span><span class="s">"Median"</span><span class="p">)</span>
<span class="n">ax</span><span class="p">.</span><span class="n">axhline</span><span class="p">(</span><span class="n">S0</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s">"black"</span><span class="p">,</span> <span class="n">ls</span><span class="o">=</span><span class="s">"--"</span><span class="p">,</span> <span class="n">lw</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">label</span><span class="o">=</span><span class="s">"$S_0$"</span><span class="p">)</span>
<span class="n">ax</span><span class="p">.</span><span class="n">set_title</span><span class="p">(</span><span class="s">"Risk-neutral price paths (fan chart)"</span><span class="p">)</span>
<span class="n">ax</span><span class="p">.</span><span class="n">set_xlabel</span><span class="p">(</span><span class="s">"Time (years)"</span><span class="p">);</span> <span class="n">ax</span><span class="p">.</span><span class="n">set_ylabel</span><span class="p">(</span><span class="s">"$S_t^Q$"</span><span class="p">);</span> <span class="n">ax</span><span class="p">.</span><span class="n">legend</span><span class="p">()</span>

<span class="c1"># European call
</span><span class="n">ax</span> <span class="o">=</span> <span class="n">axes</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
<span class="n">ax</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">strikes</span><span class="p">,</span> <span class="n">bsc</span><span class="p">,</span>     <span class="s">"o-"</span><span class="p">,</span>  <span class="n">color</span><span class="o">=</span><span class="s">"steelblue"</span><span class="p">,</span> <span class="n">label</span><span class="o">=</span><span class="s">"Black-Scholes"</span><span class="p">)</span>
<span class="n">ax</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">strikes</span><span class="p">,</span> <span class="n">mc_call</span><span class="p">,</span> <span class="s">"^--"</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s">"coral"</span><span class="p">,</span>     <span class="n">label</span><span class="o">=</span><span class="s">"Monte Carlo"</span><span class="p">)</span>
<span class="n">ax</span><span class="p">.</span><span class="n">set_title</span><span class="p">(</span><span class="s">"European call prices"</span><span class="p">)</span>
<span class="n">ax</span><span class="p">.</span><span class="n">set_xlabel</span><span class="p">(</span><span class="s">"Strike $K$"</span><span class="p">);</span> <span class="n">ax</span><span class="p">.</span><span class="n">set_ylabel</span><span class="p">(</span><span class="s">"Price"</span><span class="p">);</span> <span class="n">ax</span><span class="p">.</span><span class="n">legend</span><span class="p">()</span>

<span class="c1"># European put
</span><span class="n">ax</span> <span class="o">=</span> <span class="n">axes</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span>
<span class="n">ax</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">strikes</span><span class="p">,</span> <span class="n">bsp</span><span class="p">,</span>    <span class="s">"o-"</span><span class="p">,</span>  <span class="n">color</span><span class="o">=</span><span class="s">"steelblue"</span><span class="p">,</span> <span class="n">label</span><span class="o">=</span><span class="s">"Black-Scholes"</span><span class="p">)</span>
<span class="n">ax</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">strikes</span><span class="p">,</span> <span class="n">mc_put</span><span class="p">,</span> <span class="s">"^--"</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s">"coral"</span><span class="p">,</span>     <span class="n">label</span><span class="o">=</span><span class="s">"Monte Carlo"</span><span class="p">)</span>
<span class="n">ax</span><span class="p">.</span><span class="n">set_title</span><span class="p">(</span><span class="s">"European put prices"</span><span class="p">)</span>
<span class="n">ax</span><span class="p">.</span><span class="n">set_xlabel</span><span class="p">(</span><span class="s">"Strike $K$"</span><span class="p">);</span> <span class="n">ax</span><span class="p">.</span><span class="n">set_ylabel</span><span class="p">(</span><span class="s">"Price"</span><span class="p">);</span> <span class="n">ax</span><span class="p">.</span><span class="n">legend</span><span class="p">()</span>

<span class="n">plt</span><span class="p">.</span><span class="n">tight_layout</span><span class="p">()</span>
<span class="n">plt</span><span class="p">.</span><span class="n">show</span><span class="p">()</span>
</code></pre></div></div>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="n">Simulating</span> <span class="n">price</span> <span class="n">paths</span> <span class="err">…</span>
<span class="n">Fitting</span> <span class="n">AR</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="nb">filter</span> <span class="n">on</span> <span class="mi">250</span> <span class="n">paths</span> <span class="err">…</span>
  <span class="n">path</span> <span class="mi">200</span><span class="o">/</span><span class="mi">250</span>
<span class="n">Filter</span> <span class="n">done</span><span class="p">.</span>
<span class="n">Stationary</span> <span class="n">paths</span> <span class="n">kept</span><span class="p">:</span> <span class="mi">242</span><span class="o">/</span><span class="mi">250</span>
<span class="n">Bootstrapping</span> <span class="mi">5000</span> <span class="n">innovation</span> <span class="n">sequences</span> <span class="err">…</span>
<span class="n">Risk</span><span class="o">-</span><span class="n">neutral</span> <span class="n">price</span> <span class="n">matrix</span> <span class="n">shape</span><span class="p">:</span> <span class="p">(</span><span class="mi">1260</span><span class="p">,</span> <span class="mi">5000</span><span class="p">)</span>

<span class="o">============================================================</span>
<span class="n">I</span><span class="p">.</span> <span class="n">Basic</span> <span class="n">diagnostics</span>
<span class="o">============================================================</span>
<span class="n">Negative</span> <span class="n">prices</span> <span class="p">:</span> <span class="mi">0</span>

<span class="n">Terminal</span> <span class="n">price</span> <span class="n">S_T</span> <span class="n">summary</span><span class="p">:</span>
<span class="n">count</span>    <span class="mf">5000.0000</span>
<span class="n">mean</span>      <span class="mf">128.5454</span>
<span class="n">std</span>        <span class="mf">12.4298</span>
<span class="nb">min</span>        <span class="mf">78.0502</span>
<span class="mi">25</span><span class="o">%</span>       <span class="mf">120.1690</span>
<span class="mi">50</span><span class="o">%</span>       <span class="mf">128.4840</span>
<span class="mi">75</span><span class="o">%</span>       <span class="mf">136.8843</span>
<span class="nb">max</span>       <span class="mf">170.6847</span>
<span class="n">Skewness</span>        <span class="p">:</span> <span class="mf">0.0190</span>
<span class="n">Excess</span> <span class="n">kurtosis</span> <span class="p">:</span> <span class="mf">0.0060</span>

<span class="o">============================================================</span>
<span class="n">II</span><span class="p">.</span> <span class="n">Martingale</span> <span class="n">check</span>
<span class="o">============================================================</span>
<span class="n">S_0</span>                    <span class="p">:</span> <span class="mf">100.0</span>
<span class="n">E</span><span class="p">[</span><span class="n">exp</span><span class="p">(</span><span class="o">-</span><span class="n">rT</span><span class="p">)</span><span class="err">·</span><span class="n">S_T</span><span class="p">]</span>        <span class="p">:</span> <span class="mf">100.1112</span>
<span class="o">|</span><span class="n">error</span><span class="o">|</span>                <span class="p">:</span> <span class="mf">0.1112</span>

<span class="n">Mean</span> <span class="n">discounted</span> <span class="n">price</span> <span class="err">–</span> <span class="n">first</span> <span class="mi">6</span> <span class="n">time</span> <span class="n">points</span><span class="p">:</span>
<span class="p">[</span><span class="mf">100.</span>      <span class="mf">99.9947</span>  <span class="mf">99.994</span>   <span class="mf">99.9932</span>  <span class="mf">99.9927</span>  <span class="mf">99.997</span> <span class="p">]</span>
<span class="n">Mean</span> <span class="n">discounted</span> <span class="n">price</span> <span class="err">–</span> <span class="n">last</span> <span class="mi">6</span> <span class="n">time</span> <span class="n">points</span><span class="p">:</span>
<span class="p">[</span><span class="mf">100.1233</span> <span class="mf">100.1241</span> <span class="mf">100.1319</span> <span class="mf">100.1295</span> <span class="mf">100.1303</span> <span class="mf">100.1311</span><span class="p">]</span>

<span class="n">t</span><span class="o">-</span><span class="n">test</span> <span class="n">H0</span><span class="p">:</span> <span class="n">E</span><span class="p">[</span><span class="n">exp</span><span class="p">(</span><span class="o">-</span><span class="n">rT</span><span class="p">)</span><span class="err">·</span><span class="n">S_T</span><span class="p">]</span> <span class="o">=</span> <span class="n">S0</span>  <span class="err">→</span>  <span class="n">t</span><span class="o">=</span><span class="mf">0.8125</span><span class="p">,</span> <span class="n">p</span><span class="o">=</span><span class="mf">0.4166</span>

<span class="o">============================================================</span>
<span class="n">III</span><span class="p">.</span> <span class="n">Log</span><span class="o">-</span><span class="k">return</span> <span class="n">distribution</span>
<span class="o">============================================================</span>
<span class="n">Shapiro</span><span class="o">-</span><span class="n">Wilk</span>  <span class="p">:</span> <span class="n">W</span><span class="o">=</span><span class="mf">0.999111</span><span class="p">,</span> <span class="n">p</span><span class="o">=</span><span class="mf">1.0526e-02</span>
<span class="n">KS</span> <span class="n">vs</span> <span class="n">Normal</span>  <span class="p">:</span> <span class="n">D</span><span class="o">=</span><span class="mf">0.009983</span><span class="p">,</span> <span class="n">p</span><span class="o">=</span><span class="mf">6.9750e-01</span>

<span class="n">Mean</span> <span class="n">terminal</span> <span class="n">log</span><span class="o">-</span><span class="k">return</span>  <span class="p">:</span> <span class="mf">0.000209</span>
<span class="n">Theoretical</span> <span class="p">(</span><span class="n">r</span><span class="o">-</span><span class="n">σ</span><span class="err">²</span><span class="o">/</span><span class="mi">2</span><span class="p">)</span><span class="err">·</span><span class="n">dt</span>  <span class="p">:</span> <span class="mf">0.000195</span>

<span class="n">Var</span><span class="p">(</span><span class="n">log</span> <span class="n">S_T</span><span class="p">)</span> <span class="n">empirical</span>  <span class="p">:</span> <span class="mf">0.0096</span>
<span class="n">Var</span><span class="p">(</span><span class="n">log</span> <span class="n">S_T</span><span class="p">)</span> <span class="n">GBM</span> <span class="n">theory</span> <span class="p">:</span> <span class="mf">0.0080</span>
<span class="n">Ratio</span>                   <span class="p">:</span> <span class="mf">1.1949</span>

<span class="o">============================================================</span>
<span class="n">IV</span><span class="p">.</span> <span class="n">European</span> <span class="n">option</span> <span class="n">prices</span><span class="p">:</span> <span class="n">MC</span> <span class="n">vs</span> <span class="n">Black</span><span class="o">-</span><span class="n">Scholes</span>
<span class="o">============================================================</span>
  <span class="n">K</span>  <span class="n">BS_call</span>  <span class="n">MC_call</span>  <span class="n">err_call</span>  <span class="n">BS_put</span>  <span class="n">MC_put</span>  <span class="n">err_put</span>  <span class="n">PCP_error</span>
 <span class="mi">80</span>  <span class="mf">37.6959</span>  <span class="mf">37.8075</span>    <span class="mf">0.1115</span>  <span class="mf">0.0000</span>  <span class="mf">0.0003</span>   <span class="mf">0.0003</span>     <span class="mf">0.1112</span>
 <span class="mi">90</span>  <span class="mf">29.9080</span>  <span class="mf">30.0226</span>    <span class="mf">0.1146</span>  <span class="mf">0.0001</span>  <span class="mf">0.0034</span>   <span class="mf">0.0034</span>     <span class="mf">0.1112</span>
 <span class="mi">95</span>  <span class="mf">26.0147</span>  <span class="mf">26.1352</span>    <span class="mf">0.1206</span>  <span class="mf">0.0008</span>  <span class="mf">0.0101</span>   <span class="mf">0.0093</span>     <span class="mf">0.1112</span>
<span class="mi">100</span>  <span class="mf">22.1260</span>  <span class="mf">22.2665</span>    <span class="mf">0.1405</span>  <span class="mf">0.0061</span>  <span class="mf">0.0353</span>   <span class="mf">0.0292</span>     <span class="mf">0.1112</span>
<span class="mi">105</span>  <span class="mf">18.2602</span>  <span class="mf">18.4462</span>    <span class="mf">0.1860</span>  <span class="mf">0.0343</span>  <span class="mf">0.1090</span>   <span class="mf">0.0748</span>     <span class="mf">0.1112</span>
<span class="mi">110</span>  <span class="mf">14.4727</span>  <span class="mf">14.7258</span>    <span class="mf">0.2531</span>  <span class="mf">0.1407</span>  <span class="mf">0.2826</span>   <span class="mf">0.1419</span>     <span class="mf">0.1112</span>
<span class="mi">120</span>   <span class="mf">7.6644</span>   <span class="mf">8.0484</span>    <span class="mf">0.3840</span>  <span class="mf">1.1205</span>  <span class="mf">1.3932</span>   <span class="mf">0.2727</span>     <span class="mf">0.1112</span>
</code></pre></div></div>

<p><img src="/images/2026-03-22/2026-03-22-image1.png" alt="image-title-here" class="img-responsive" /></p>]]></content><author><name></name></author><category term="Python" /><summary type="html"><![CDATA[Python version of 'Option pricing using time series models as market price of risk' and resampling]]></summary></entry><entry><title type="html">Option pricing using time series models as market price of risk Pt.3</title><link href="https://thierrymoudiki.github.io/blog/2026/03/16/r/Semi-parametric-MarketPriceofRisk-update" rel="alternate" type="text/html" title="Option pricing using time series models as market price of risk Pt.3" /><published>2026-03-16T00:00:00+00:00</published><updated>2026-03-16T00:00:00+00:00</updated><id>https://thierrymoudiki.github.io/blog/2026/03/16/r/Semi-parametric-MarketPriceofRisk-update</id><content type="html" xml:base="https://thierrymoudiki.github.io/blog/2026/03/16/r/Semi-parametric-MarketPriceofRisk-update"><![CDATA[<p>This post is the third part of <a href="https://thierrymoudiki.github.io/blog/2025/12/07/r/forecasting/ARIMA-Pricing">https://thierrymoudiki.github.io/blog/2025/12/07/r/forecasting/ARIMA-Pricing</a> and <a href="https://thierrymoudiki.github.io/blog/2026/02/01/r/Semi-parametric-MarketPriceofRisk-Theta">https://thierrymoudiki.github.io/blog/2026/02/01/r/Semi-parametric-MarketPriceofRisk-Theta</a>. These posts showed how to use ARIMA and Theta as market price of risk, to then price options under a risk-neutral measure by resampling <em>martingale</em> innovations.</p>

<p>After thinking about it more, here’s a condensed version of the previous posts, with some formulas and rich R code examples.</p>

<h2 id="1-market-setting">1. Market setting</h2>

<p>Let</p>

<ul>
  <li>\(S_t\) = asset price</li>
  <li>\(r\) = risk-free rate</li>
  <li>\(T\) = maturity</li>
</ul>

<p>Define the <strong>discounted price process</strong></p>

\[D_t = e^{-rt} S_t\]

<p>Under the no-arbitrage principle (Fundamental Theorem of Asset Pricing), there exists a probability measure \(Q\) such that</p>

\[E_Q[D_t \mid \mathcal{F}_{t-1}] = D_{t-1}\]

<p>so \(D_t\) is a <strong>martingale</strong>.</p>

<h2 id="2-empirical-innovation-extraction">2. Empirical innovation extraction</h2>

<p>Given simulated or observed price paths \(S_t\), compute</p>

\[D_t = e^{-rt} S_t\]

<p>Define increments</p>

\[\Delta D_t = D_t - D_{t-1}\]

<p>Fit a time-series filter</p>

\[\Delta D_t = f(\Delta D_{t-1}, \ldots, \Delta D_{t-p}) + \varepsilon_t\]

<p>where</p>

\[E[\varepsilon_t] = 0\]

<h2 id="3-bootstrap-innovation-distribution">3. Bootstrap innovation distribution</h2>

<p>Let</p>

\[\{\varepsilon_1, \ldots, \varepsilon_T\}\]

<p>be the empirical innovations.</p>

<p>Generate bootstrap resamples</p>

\[\varepsilon_t^{(i)}, \quad i = 1, \ldots, N\]

<p>using stationary bootstrap. These sequences define the <strong>innovation law</strong>.</p>

<h2 id="4-martingale-reconstruction">4. Martingale reconstruction</h2>

<p>Define the discounted process recursively:</p>

\[D_0 = S_0\]

\[D_t = D_{t-1} + \varepsilon_t\]

<p>which implies</p>

\[D_t = S_0 + \sum_{i=1}^{t} \varepsilon_i\]

<p>Since</p>

\[E[\varepsilon_t] = 0\]

<p>we obtain</p>

\[E[D_t] = E[S_0 + \sum_{i=1}^{t} \varepsilon_i] = S_0 + \sum_{i=1}^{t} E[\varepsilon_i] = S_0\]

<h2 id="5-risk-neutral-price-process">5. Risk-neutral price process</h2>

<p>Recover the price process</p>

\[S_t = e^{rt} D_t\]

<p>Then</p>

\[E[e^{-rt} S_t] = S_0\]

<p>which satisfies the <strong>risk-neutral condition</strong>.</p>

<h2 id="6-monte-carlo-pricing">6. Monte Carlo pricing</h2>

<p>For payoff \(H(S_T)\), the derivative price is</p>

\[V_0 = e^{-rT} E_Q[H(S_T)]\]

<p>Estimated by Monte Carlo:</p>

\[V_0 \approx e^{-rT} \frac{1}{N}
\sum_{i=1}^{N} H(S_T^{(i)})\]

<p>Example (European call):</p>

\[C_0 = e^{-rT} E_Q[\max(S_T - K, 0)]\]

<p>Here’s the R code for the whole process:</p>

<div class="language-R highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">library</span><span class="p">(</span><span class="n">esgtoolkit</span><span class="p">)</span><span class="w">
</span><span class="n">library</span><span class="p">(</span><span class="n">forecast</span><span class="p">)</span><span class="w">

</span><span class="n">set.seed</span><span class="p">(</span><span class="m">123</span><span class="p">)</span><span class="w">
</span><span class="n">n</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">250L</span><span class="w">
</span><span class="n">h</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">5</span><span class="w">
</span><span class="n">freq</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="s2">"daily"</span><span class="w">
</span><span class="n">r</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">0.05</span><span class="w">
</span><span class="n">maturity</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">5</span><span class="w">
</span><span class="n">S0</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">100</span><span class="w">
</span><span class="n">mu</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">0.08</span><span class="w">
</span><span class="n">sigma</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">0.04</span><span class="w">
</span><span class="n">n_sims</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">5000L</span><span class="w">

</span><span class="c1"># Simulate under physical measure with stochastic volatility and jumps</span><span class="w">
</span><span class="n">sim_GBM</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">esgtoolkit</span><span class="o">::</span><span class="n">simdiff</span><span class="p">(</span><span class="w">
  </span><span class="n">n</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">n</span><span class="p">,</span><span class="w">
  </span><span class="n">horizon</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">h</span><span class="p">,</span><span class="w">
  </span><span class="n">frequency</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">freq</span><span class="p">,</span><span class="w">
  </span><span class="n">x0</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">S0</span><span class="p">,</span><span class="w">
  </span><span class="n">theta1</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">mu</span><span class="p">,</span><span class="w">
  </span><span class="n">theta2</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">sigma</span><span class="w">
</span><span class="p">)</span><span class="w">
</span><span class="n">sim_SVJD</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">esgtoolkit</span><span class="o">::</span><span class="n">rsvjd</span><span class="p">(</span><span class="n">n</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">n</span><span class="p">,</span><span class="w"> </span><span class="n">r0</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">mu</span><span class="p">)</span><span class="w">
</span><span class="n">sim_Heston</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">esgtoolkit</span><span class="o">::</span><span class="n">rsvjd</span><span class="p">(</span><span class="w">
  </span><span class="n">n</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">n</span><span class="p">,</span><span class="w">
  </span><span class="n">r0</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">mu</span><span class="p">,</span><span class="w">
  </span><span class="n">lambda</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0</span><span class="p">,</span><span class="w">
  </span><span class="n">mu_J</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0</span><span class="p">,</span><span class="w">
  </span><span class="n">sigma_J</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0</span><span class="w">
</span><span class="p">)</span><span class="w">


</span><span class="c1"># This exp(-r*t)*S_t</span><span class="w">
</span><span class="n">discounted_prices_GBM</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">esgtoolkit</span><span class="o">::</span><span class="n">esgdiscountfactor</span><span class="p">(</span><span class="n">r</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">r</span><span class="p">,</span><span class="w"> </span><span class="n">X</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">sim_GBM</span><span class="p">)</span><span class="w">
</span><span class="n">discounted_prices_SVJD</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">esgtoolkit</span><span class="o">::</span><span class="n">esgdiscountfactor</span><span class="p">(</span><span class="n">r</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">r</span><span class="p">,</span><span class="w"> </span><span class="n">X</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">sim_SVJD</span><span class="p">)</span><span class="w">
</span><span class="n">discounted_prices_Heston</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">esgtoolkit</span><span class="o">::</span><span class="n">esgdiscountfactor</span><span class="p">(</span><span class="n">r</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">r</span><span class="p">,</span><span class="w"> </span><span class="n">X</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">sim_Heston</span><span class="p">)</span><span class="w">

</span><span class="c1"># Take the first difference of exp(-r*t)*S_t</span><span class="w">
</span><span class="c1"># (we want a center first difference in Q)</span><span class="w">
</span><span class="n">diff_martingale_GBM</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">diff</span><span class="p">(</span><span class="n">discounted_prices_GBM</span><span class="p">)</span><span class="w">
</span><span class="n">diff_martingale_Heston</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">diff</span><span class="p">(</span><span class="n">discounted_prices_Heston</span><span class="p">)</span><span class="w">
</span><span class="n">diff_martingale_SVJD</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">diff</span><span class="p">(</span><span class="n">discounted_prices_SVJD</span><span class="p">)</span><span class="w">


</span><span class="c1"># Adjust a time series filter the martingale difference</span><span class="w">

</span><span class="n">choice_process</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="s2">"GBM"</span><span class="w">
</span><span class="n">choice_filter</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="s2">"auto.arima"</span><span class="w">

</span><span class="n">diff_martingale</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">switch</span><span class="p">(</span><span class="n">choice_process</span><span class="p">,</span><span class="w">
                          </span><span class="n">GBM</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">diff_martingale_GBM</span><span class="p">,</span><span class="w">
                          </span><span class="n">Heston</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">diff_martingale_Heston</span><span class="p">,</span><span class="w">
                          </span><span class="n">SVJD</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">diff_martingale_SVJD</span><span class="p">)</span><span class="w">

</span><span class="n">n_dates</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">nrow</span><span class="p">(</span><span class="n">diff_martingale</span><span class="p">)</span><span class="w">
</span><span class="n">n_dates_1</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">n_dates</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="m">1</span><span class="w">
</span><span class="n">resids_matrix</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">matrix</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="w"> </span><span class="n">nrow</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">n_dates_1</span><span class="p">,</span><span class="w"> </span><span class="n">ncol</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">n</span><span class="p">)</span><span class="w">

</span><span class="n">pb</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">utils</span><span class="o">::</span><span class="n">txtProgressBar</span><span class="p">(</span><span class="n">min</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0</span><span class="p">,</span><span class="w"> </span><span class="n">max</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">n</span><span class="p">,</span><span class="w"> </span><span class="n">style</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">3L</span><span class="p">)</span><span class="w">

</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">choice_filter</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="s2">"AR(1)"</span><span class="p">)</span><span class="w">
</span><span class="p">{</span><span class="w">
  </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">j</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">n</span><span class="p">)</span><span class="w">
  </span><span class="p">{</span><span class="w">
    </span><span class="n">y</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">diff_martingale</span><span class="p">[</span><span class="m">-1</span><span class="p">,</span><span class="w"> </span><span class="n">j</span><span class="p">]</span><span class="w">
    </span><span class="n">X</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">matrix</span><span class="p">(</span><span class="n">diff_martingale</span><span class="p">[</span><span class="nf">seq_len</span><span class="p">(</span><span class="n">n_dates_1</span><span class="p">),</span><span class="w"> </span><span class="n">j</span><span class="p">],</span><span class="w"> </span><span class="n">ncol</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">1</span><span class="p">)</span><span class="w">
    </span><span class="n">fit_lm</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">.lm.fit</span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">X</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">y</span><span class="p">)</span><span class="w">
    </span><span class="n">fitted_values</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">X</span><span class="w"> </span><span class="o">%*%</span><span class="w"> </span><span class="n">fit_lm</span><span class="o">$</span><span class="n">coef</span><span class="w">
    </span><span class="n">resids_matrix</span><span class="p">[,</span><span class="w"> </span><span class="n">j</span><span class="p">]</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">fitted_values</span><span class="w">
    </span><span class="n">utils</span><span class="o">::</span><span class="n">setTxtProgressBar</span><span class="p">(</span><span class="n">pb</span><span class="p">,</span><span class="w"> </span><span class="n">j</span><span class="p">)</span><span class="w">
  </span><span class="p">}</span><span class="w">
  </span><span class="n">close</span><span class="p">(</span><span class="n">pb</span><span class="p">)</span><span class="w">
</span><span class="p">}</span><span class="w"> 

</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">choice_filter</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="s2">"auto.arima"</span><span class="p">){</span><span class="w">
  </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">j</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">n</span><span class="p">)</span><span class="w">
  </span><span class="p">{</span><span class="w">
    </span><span class="n">y</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">diff_martingale</span><span class="p">[</span><span class="m">-1</span><span class="p">,</span><span class="w"> </span><span class="n">j</span><span class="p">]</span><span class="w">
    </span><span class="n">resids_matrix</span><span class="p">[,</span><span class="w"> </span><span class="n">j</span><span class="p">]</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">residuals</span><span class="p">(</span><span class="n">auto.arima</span><span class="p">(</span><span class="n">y</span><span class="p">,</span><span class="w"> </span><span class="n">allowmean</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">FALSE</span><span class="p">))</span><span class="w">
    </span><span class="n">utils</span><span class="o">::</span><span class="n">setTxtProgressBar</span><span class="p">(</span><span class="n">pb</span><span class="p">,</span><span class="w"> </span><span class="n">j</span><span class="p">)</span><span class="w">
  </span><span class="p">}</span><span class="w">
  </span><span class="n">close</span><span class="p">(</span><span class="n">pb</span><span class="p">)</span><span class="w">
</span><span class="p">}</span><span class="w">

</span><span class="n">pvals</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">sapply</span><span class="p">(</span><span class="m">1</span><span class="o">:</span><span class="n">n</span><span class="p">,</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">j</span><span class="p">)</span><span class="w">
  </span><span class="n">Box.test</span><span class="p">(</span><span class="n">resids_matrix</span><span class="p">[,</span><span class="w"> </span><span class="n">j</span><span class="p">],</span><span class="w"> </span><span class="n">type</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Ljung-Box"</span><span class="p">)</span><span class="o">$</span><span class="n">p.value</span><span class="p">)</span><span class="w">

</span><span class="c1"># Keep only stationary residuals (non-reject null at 5% level)</span><span class="w">
</span><span class="n">stationary_cols</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">which</span><span class="p">(</span><span class="n">pvals</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="m">0.05</span><span class="p">)</span><span class="w">
</span><span class="n">resids_stationary</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">resids_matrix</span><span class="p">[,</span><span class="w"> </span><span class="n">stationary_cols</span><span class="p">]</span><span class="w">
</span><span class="n">print</span><span class="p">(</span><span class="nf">dim</span><span class="p">(</span><span class="n">resids_stationary</span><span class="p">))</span><span class="w">

</span><span class="n">centered_resids_stationary</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">scale</span><span class="p">(</span><span class="n">resids_stationary</span><span class="p">,</span><span class="w"> </span><span class="n">center</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">,</span><span class="w"> </span><span class="n">scale</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">FALSE</span><span class="p">)[,</span><span class="w"> </span><span class="p">]</span><span class="w">
</span><span class="n">centered_resids_stationary</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">ts</span><span class="p">(</span><span class="n">centered_resids_stationary</span><span class="p">,</span><span class="w">
                                 </span><span class="n">end</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">end</span><span class="p">(</span><span class="n">sim_GBM</span><span class="p">),</span><span class="w">
                                 </span><span class="n">frequency</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">frequency</span><span class="p">(</span><span class="n">sim_GBM</span><span class="p">))</span><span class="w">

</span><span class="c1"># resample_centered_resids has nrow = number of dates, ncol = n_sims</span><span class="w">
</span><span class="n">resampled_centered_resids</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">list</span><span class="p">()</span><span class="w">

</span><span class="n">n_resids_stationary</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">dim</span><span class="p">(</span><span class="n">resids_stationary</span><span class="p">)[</span><span class="m">2</span><span class="p">]</span><span class="w">

</span><span class="n">n_times</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">ceiling</span><span class="p">(</span><span class="n">n_sims</span><span class="o">/</span><span class="n">n_resids_stationary</span><span class="p">)</span><span class="w">
</span><span class="n">pb</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">utils</span><span class="o">::</span><span class="n">txtProgressBar</span><span class="p">(</span><span class="n">min</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0</span><span class="p">,</span><span class="w"> </span><span class="n">max</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">n_times</span><span class="p">,</span><span class="w"> </span><span class="n">style</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">3L</span><span class="p">)</span><span class="w">
</span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">i</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="nf">seq_len</span><span class="p">(</span><span class="n">n_times</span><span class="p">))</span><span class="w">
</span><span class="p">{</span><span class="w">
  </span><span class="n">set.seed</span><span class="p">(</span><span class="m">123</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">i</span><span class="o">*</span><span class="m">100</span><span class="p">)</span><span class="w">
  </span><span class="n">resampled_centered_resids</span><span class="p">[[</span><span class="n">i</span><span class="p">]]</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">apply</span><span class="p">(</span><span class="n">centered_resids_stationary</span><span class="p">,</span><span class="w"> </span><span class="m">2</span><span class="p">,</span><span class="w"> 
                                         </span><span class="k">function</span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="n">tseries</span><span class="o">::</span><span class="n">tsbootstrap</span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">nb</span><span class="o">=</span><span class="m">1</span><span class="p">,</span><span class="w"> 
                                                                          </span><span class="n">type</span><span class="o">=</span><span class="s2">"stationary"</span><span class="p">))</span><span class="w">
  </span><span class="n">utils</span><span class="o">::</span><span class="n">setTxtProgressBar</span><span class="p">(</span><span class="n">pb</span><span class="p">,</span><span class="w"> </span><span class="n">i</span><span class="p">)</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="n">close</span><span class="p">(</span><span class="n">pb</span><span class="p">)</span><span class="w">


</span><span class="n">resampled_centered_resids_matrix</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">do.call</span><span class="p">(</span><span class="n">cbind</span><span class="p">,</span><span class="w"> </span><span class="n">resampled_centered_resids</span><span class="p">)[,</span><span class="w"> </span><span class="nf">seq_len</span><span class="p">(</span><span class="n">n_sims</span><span class="p">)]</span><span class="w">
</span><span class="c1"># Convert to ts object with proper time attributes</span><span class="w">
</span><span class="n">resampled_ts</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">ts</span><span class="p">(</span><span class="n">resampled_centered_resids_matrix</span><span class="p">,</span><span class="w">
                   </span><span class="n">start</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">start</span><span class="p">(</span><span class="n">centered_resids_stationary</span><span class="p">),</span><span class="w">
                   </span><span class="n">end</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">end</span><span class="p">(</span><span class="n">centered_resids_stationary</span><span class="p">),</span><span class="w">
                   </span><span class="n">frequency</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">frequency</span><span class="p">(</span><span class="n">centered_resids_stationary</span><span class="p">))</span><span class="w">

</span><span class="c1"># Check dimensions: should be (n_dates-1) x n_sims</span><span class="w">
</span><span class="n">print</span><span class="p">(</span><span class="nf">dim</span><span class="p">(</span><span class="n">resampled_ts</span><span class="p">))</span><span class="w">

</span><span class="c1"># At time t = 0, diff_martingale process is equal is D_0 = S_0 (exp(-r * 0)*S_0)</span><span class="w">
</span><span class="c1"># First cumsum the process to get exp(-r*t)*S_t</span><span class="w">
</span><span class="c1"># Then multiply by exp(r*t) to have a process in risk neutral probability</span><span class="w">

</span><span class="c1"># Step 1: Start with S0 at t=0</span><span class="w">
</span><span class="n">D0</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">S0</span><span class="w">  </span><span class="c1"># since exp(-r*0) = 1</span><span class="w">

</span><span class="c1"># Step 2: Cumsum to get discounted prices (e^{-rt} * S_t) under Q</span><span class="w">
</span><span class="c1"># Add D0 as first row, then cumsum of innovations </span><span class="w">
</span><span class="n">discounted_paths</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">D0</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">apply</span><span class="p">(</span><span class="n">resampled_ts</span><span class="p">,</span><span class="w"> </span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="n">cumsum</span><span class="p">)</span><span class="w">  </span><span class="c1"># t=1..T</span><span class="w">
</span><span class="n">discounted_paths</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">rbind</span><span class="p">(</span><span class="n">D0</span><span class="p">,</span><span class="w"> </span><span class="n">discounted_paths</span><span class="p">)</span><span class="w">
</span><span class="n">discounted_paths</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">ts</span><span class="p">(</span><span class="n">as.matrix</span><span class="p">(</span><span class="n">discounted_paths</span><span class="p">),</span><span class="w"> </span><span class="n">start</span><span class="o">=</span><span class="n">start</span><span class="p">(</span><span class="n">sim_GBM</span><span class="p">),</span><span class="w"> 
                       </span><span class="n">frequency</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">frequency</span><span class="p">(</span><span class="n">sim_GBM</span><span class="p">))</span><span class="w">

</span><span class="n">time_points</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">time</span><span class="p">(</span><span class="n">discounted_paths</span><span class="p">)</span><span class="w">
</span><span class="n">risk_neutral_prices</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">ts</span><span class="p">(</span><span class="n">discounted_paths</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="nf">exp</span><span class="p">(</span><span class="n">r</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">time_points</span><span class="p">),</span><span class="w"> 
                          </span><span class="n">start</span><span class="o">=</span><span class="n">start</span><span class="p">(</span><span class="n">sim_GBM</span><span class="p">),</span><span class="w"> 
                          </span><span class="n">frequency</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">frequency</span><span class="p">(</span><span class="n">sim_GBM</span><span class="p">))</span><span class="w">

</span><span class="n">head</span><span class="p">(</span><span class="n">risk_neutral_prices</span><span class="p">[,</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="m">5</span><span class="p">])</span><span class="w">

</span><span class="n">esgplotbands</span><span class="p">(</span><span class="n">risk_neutral_prices</span><span class="p">)</span><span class="w">


</span><span class="c1"># =============================================================================</span><span class="w">
</span><span class="c1"># I. Basic diagnostics</span><span class="w">
</span><span class="c1"># =============================================================================</span><span class="w">


</span><span class="c1"># No negative prices (log-normal support check)</span><span class="w">
</span><span class="n">n_negative</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">sum</span><span class="p">(</span><span class="n">risk_neutral_prices</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="m">0</span><span class="p">,</span><span class="w"> </span><span class="n">na.rm</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">)</span><span class="w">
</span><span class="n">cat</span><span class="p">(</span><span class="s2">"Negative prices:"</span><span class="p">,</span><span class="w"> </span><span class="n">n_negative</span><span class="p">,</span><span class="w"> </span><span class="s2">"\n"</span><span class="p">)</span><span class="w">
</span><span class="n">stopifnot</span><span class="p">(</span><span class="n">n_negative</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="m">0</span><span class="p">)</span><span class="w">

</span><span class="c1"># Terminal distribution summary</span><span class="w">
</span><span class="n">S_T</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">as.numeric</span><span class="p">(</span><span class="n">risk_neutral_prices</span><span class="p">[</span><span class="n">nrow</span><span class="p">(</span><span class="n">risk_neutral_prices</span><span class="p">),</span><span class="w"> </span><span class="p">])</span><span class="w">
</span><span class="n">cat</span><span class="p">(</span><span class="s2">"\n--- Terminal price S_T summary ---\n"</span><span class="p">)</span><span class="w">
</span><span class="n">print</span><span class="p">(</span><span class="n">summary</span><span class="p">(</span><span class="n">S_T</span><span class="p">))</span><span class="w">
</span><span class="n">cat</span><span class="p">(</span><span class="s2">"Std dev:"</span><span class="p">,</span><span class="w"> </span><span class="n">sd</span><span class="p">(</span><span class="n">S_T</span><span class="p">),</span><span class="w"> </span><span class="s2">"\n"</span><span class="p">)</span><span class="w">
</span><span class="n">cat</span><span class="p">(</span><span class="s2">"Skewness:"</span><span class="p">,</span><span class="w"> </span><span class="n">moments</span><span class="o">::</span><span class="n">skewness</span><span class="p">(</span><span class="n">S_T</span><span class="p">),</span><span class="w"> </span><span class="s2">"\n"</span><span class="p">)</span><span class="w">
</span><span class="n">cat</span><span class="p">(</span><span class="s2">"Excess kurtosis:"</span><span class="p">,</span><span class="w"> </span><span class="n">moments</span><span class="o">::</span><span class="n">kurtosis</span><span class="p">(</span><span class="n">S_T</span><span class="p">)</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="m">3</span><span class="p">,</span><span class="w"> </span><span class="s2">"\n"</span><span class="p">)</span><span class="w">

</span><span class="c1"># =============================================================================</span><span class="w">
</span><span class="c1"># II. Martingale checks</span><span class="w">
</span><span class="c1"># =============================================================================</span><span class="w">

</span><span class="c1"># E[exp(-r*T) * S_T] should equal S_0</span><span class="w">
</span><span class="n">T_years</span><span class="w">    </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">h</span><span class="w">
</span><span class="n">discount_T</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">exp</span><span class="p">(</span><span class="o">-</span><span class="n">r</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">T_years</span><span class="p">)</span><span class="w">
</span><span class="n">mc_price</span><span class="w">   </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">discount_T</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">mean</span><span class="p">(</span><span class="n">S_T</span><span class="p">)</span><span class="w">
</span><span class="n">cat</span><span class="p">(</span><span class="s2">"\n--- Martingale check ---\n"</span><span class="p">)</span><span class="w">
</span><span class="n">cat</span><span class="p">(</span><span class="s2">"S_0                       :"</span><span class="p">,</span><span class="w"> </span><span class="n">S0</span><span class="p">,</span><span class="w"> </span><span class="s2">"\n"</span><span class="p">)</span><span class="w">
</span><span class="n">cat</span><span class="p">(</span><span class="s2">"E[exp(-rT) * S_T]         :"</span><span class="p">,</span><span class="w"> </span><span class="nf">round</span><span class="p">(</span><span class="n">mc_price</span><span class="p">,</span><span class="w"> </span><span class="m">4</span><span class="p">),</span><span class="w"> </span><span class="s2">"\n"</span><span class="p">)</span><span class="w">
</span><span class="n">cat</span><span class="p">(</span><span class="s2">"Absolute error            :"</span><span class="p">,</span><span class="w"> </span><span class="nf">round</span><span class="p">(</span><span class="nf">abs</span><span class="p">(</span><span class="n">mc_price</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">S0</span><span class="p">),</span><span class="w"> </span><span class="m">4</span><span class="p">),</span><span class="w"> </span><span class="s2">"\n"</span><span class="p">)</span><span class="w">

</span><span class="c1"># Check at every time point: mean discounted path should stay ~S0</span><span class="w">
</span><span class="n">discounted_paths_check</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">risk_neutral_prices</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="nf">exp</span><span class="p">(</span><span class="o">-</span><span class="n">r</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">time</span><span class="p">(</span><span class="n">risk_neutral_prices</span><span class="p">))</span><span class="w">
</span><span class="n">mean_discounted</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">rowMeans</span><span class="p">(</span><span class="n">discounted_paths_check</span><span class="p">)</span><span class="w">
</span><span class="n">cat</span><span class="p">(</span><span class="s2">"\nMean discounted price (first 6 and last 6 time points):\n"</span><span class="p">)</span><span class="w">
</span><span class="n">print</span><span class="p">(</span><span class="nf">round</span><span class="p">(</span><span class="n">head</span><span class="p">(</span><span class="n">mean_discounted</span><span class="p">),</span><span class="w"> </span><span class="m">4</span><span class="p">))</span><span class="w">
</span><span class="n">print</span><span class="p">(</span><span class="nf">round</span><span class="p">(</span><span class="n">tail</span><span class="p">(</span><span class="n">mean_discounted</span><span class="p">),</span><span class="w"> </span><span class="m">4</span><span class="p">))</span><span class="w">

</span><span class="c1"># t-test: is E[exp(-rT)*S_T] = S0?</span><span class="w">
</span><span class="n">ttest</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">t.test</span><span class="p">(</span><span class="n">discount_T</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">S_T</span><span class="o">-</span><span class="n">S0</span><span class="p">,</span><span class="w"> </span><span class="n">mu</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0</span><span class="p">)</span><span class="w">
</span><span class="n">cat</span><span class="p">(</span><span class="s2">"\nt-test H0: E[exp(-rT)*S_T] = S0\n"</span><span class="p">)</span><span class="w">
</span><span class="n">print</span><span class="p">(</span><span class="n">ttest</span><span class="p">)</span><span class="w">

</span><span class="c1"># =============================================================================</span><span class="w">
</span><span class="c1"># III. Distributional tests on log-returns</span><span class="w">
</span><span class="c1"># =============================================================================</span><span class="w">

</span><span class="n">log_returns</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">diff</span><span class="p">(</span><span class="nf">log</span><span class="p">(</span><span class="n">risk_neutral_prices</span><span class="p">))</span><span class="w">

</span><span class="c1"># Normality of cross-sectional log-returns at terminal date</span><span class="w">
</span><span class="n">lr_T</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">as.numeric</span><span class="p">(</span><span class="n">log_returns</span><span class="p">[</span><span class="n">nrow</span><span class="p">(</span><span class="n">log_returns</span><span class="p">),</span><span class="w"> </span><span class="p">])</span><span class="w">
</span><span class="n">cat</span><span class="p">(</span><span class="s2">"\n--- Normality tests on terminal log-returns ---\n"</span><span class="p">)</span><span class="w">
</span><span class="n">print</span><span class="p">(</span><span class="n">shapiro.test</span><span class="p">(</span><span class="n">sample</span><span class="p">(</span><span class="n">lr_T</span><span class="p">,</span><span class="w"> </span><span class="nf">min</span><span class="p">(</span><span class="m">5000L</span><span class="p">,</span><span class="w"> </span><span class="nf">length</span><span class="p">(</span><span class="n">lr_T</span><span class="p">)))))</span><span class="w">  </span><span class="c1"># Shapiro-Wilk (max n=5000)</span><span class="w">
</span><span class="n">print</span><span class="p">(</span><span class="n">ks.test</span><span class="p">(</span><span class="n">lr_T</span><span class="p">,</span><span class="w"> </span><span class="s2">"pnorm"</span><span class="p">,</span><span class="w"> </span><span class="n">mean</span><span class="p">(</span><span class="n">lr_T</span><span class="p">),</span><span class="w"> </span><span class="n">sd</span><span class="p">(</span><span class="n">lr_T</span><span class="p">)))</span><span class="w">          </span><span class="c1"># KS vs normal</span><span class="w">

</span><span class="c1"># Mean log-return should be close to (r - 0.5*sigma^2) * dt</span><span class="w">
</span><span class="n">dt</span><span class="w">          </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">1</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="n">frequency</span><span class="p">(</span><span class="n">risk_neutral_prices</span><span class="p">)</span><span class="w">
</span><span class="n">mean_lr</span><span class="w">     </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">mean</span><span class="p">(</span><span class="n">lr_T</span><span class="p">)</span><span class="w">
</span><span class="n">theoretical</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="p">(</span><span class="n">r</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="m">0.5</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">sigma</span><span class="o">^</span><span class="m">2</span><span class="p">)</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">dt</span><span class="w">
</span><span class="n">cat</span><span class="p">(</span><span class="s2">"\nMean terminal log-return  :"</span><span class="p">,</span><span class="w"> </span><span class="nf">round</span><span class="p">(</span><span class="n">mean_lr</span><span class="p">,</span><span class="w"> </span><span class="m">6</span><span class="p">),</span><span class="w"> </span><span class="s2">"\n"</span><span class="p">)</span><span class="w">
</span><span class="n">cat</span><span class="p">(</span><span class="s2">"Theoretical (r-s²/2)*dt   :"</span><span class="p">,</span><span class="w"> </span><span class="nf">round</span><span class="p">(</span><span class="n">theoretical</span><span class="p">,</span><span class="w"> </span><span class="m">6</span><span class="p">),</span><span class="w"> </span><span class="s2">"\n"</span><span class="p">)</span><span class="w">

</span><span class="c1"># Variance ratio: empirical vs GBM theoretical</span><span class="w">
</span><span class="c1"># Under GBM: Var(log S_T) = sigma^2 * T</span><span class="w">
</span><span class="n">empirical_var</span><span class="w">  </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">var</span><span class="p">(</span><span class="nf">log</span><span class="p">(</span><span class="n">S_T</span><span class="p">))</span><span class="w">
</span><span class="n">theoretical_var</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">sigma</span><span class="o">^</span><span class="m">2</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">T_years</span><span class="w">
</span><span class="n">cat</span><span class="p">(</span><span class="s2">"\n--- Variance ratio check ---\n"</span><span class="p">)</span><span class="w">
</span><span class="n">cat</span><span class="p">(</span><span class="s2">"Var(log S_T) empirical  :"</span><span class="p">,</span><span class="w"> </span><span class="nf">round</span><span class="p">(</span><span class="n">empirical_var</span><span class="p">,</span><span class="w"> </span><span class="m">4</span><span class="p">),</span><span class="w"> </span><span class="s2">"\n"</span><span class="p">)</span><span class="w">
</span><span class="n">cat</span><span class="p">(</span><span class="s2">"Var(log S_T) GBM theory :"</span><span class="p">,</span><span class="w"> </span><span class="nf">round</span><span class="p">(</span><span class="n">theoretical_var</span><span class="p">,</span><span class="w"> </span><span class="m">4</span><span class="p">),</span><span class="w"> </span><span class="s2">"\n"</span><span class="p">)</span><span class="w">
</span><span class="n">cat</span><span class="p">(</span><span class="s2">"Ratio                   :"</span><span class="p">,</span><span class="w"> </span><span class="nf">round</span><span class="p">(</span><span class="n">empirical_var</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="n">theoretical_var</span><span class="p">,</span><span class="w"> </span><span class="m">4</span><span class="p">),</span><span class="w"> </span><span class="s2">"\n"</span><span class="p">)</span><span class="w">

</span><span class="c1"># =============================================================================</span><span class="w">
</span><span class="c1"># IV. Option pricing — European calls and puts</span><span class="w">
</span><span class="c1"># =============================================================================</span><span class="w">

</span><span class="n">strikes</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="m">80</span><span class="p">,</span><span class="w"> </span><span class="m">90</span><span class="p">,</span><span class="w"> </span><span class="m">95</span><span class="p">,</span><span class="w"> </span><span class="m">100</span><span class="p">,</span><span class="w"> </span><span class="m">105</span><span class="p">,</span><span class="w"> </span><span class="m">110</span><span class="p">,</span><span class="w"> </span><span class="m">120</span><span class="p">)</span><span class="w">  </span><span class="c1"># ITM to OTM</span><span class="w">

</span><span class="c1"># -- Monte Carlo prices -------------------------------------------------------</span><span class="w">
</span><span class="n">mc_call</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">sapply</span><span class="p">(</span><span class="n">strikes</span><span class="p">,</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">K</span><span class="p">)</span><span class="w">
  </span><span class="nf">exp</span><span class="p">(</span><span class="o">-</span><span class="n">r</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">T_years</span><span class="p">)</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">mean</span><span class="p">(</span><span class="n">pmax</span><span class="p">(</span><span class="n">S_T</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">K</span><span class="p">,</span><span class="w"> </span><span class="m">0</span><span class="p">)))</span><span class="w">

</span><span class="n">mc_put</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">sapply</span><span class="p">(</span><span class="n">strikes</span><span class="p">,</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">K</span><span class="p">)</span><span class="w">
  </span><span class="nf">exp</span><span class="p">(</span><span class="o">-</span><span class="n">r</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">T_years</span><span class="p">)</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">mean</span><span class="p">(</span><span class="n">pmax</span><span class="p">(</span><span class="n">K</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">S_T</span><span class="p">,</span><span class="w"> </span><span class="m">0</span><span class="p">)))</span><span class="w">

</span><span class="c1"># -- Black-Scholes prices -----------------------------------------------------</span><span class="w">
</span><span class="n">bs_call</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">S</span><span class="p">,</span><span class="w"> </span><span class="n">K</span><span class="p">,</span><span class="w"> </span><span class="n">r</span><span class="p">,</span><span class="w"> </span><span class="n">sigma</span><span class="p">,</span><span class="w"> </span><span class="nb">T</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="n">d1</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="p">(</span><span class="nf">log</span><span class="p">(</span><span class="n">S</span><span class="o">/</span><span class="n">K</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="p">(</span><span class="n">r</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="m">0.5</span><span class="o">*</span><span class="n">sigma</span><span class="o">^</span><span class="m">2</span><span class="p">)</span><span class="o">*</span><span class="nb">T</span><span class="p">)</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="p">(</span><span class="n">sigma</span><span class="o">*</span><span class="nf">sqrt</span><span class="p">(</span><span class="nb">T</span><span class="p">))</span><span class="w">
  </span><span class="n">d2</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">d1</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">sigma</span><span class="o">*</span><span class="nf">sqrt</span><span class="p">(</span><span class="nb">T</span><span class="p">)</span><span class="w">
  </span><span class="n">S</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">pnorm</span><span class="p">(</span><span class="n">d1</span><span class="p">)</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">K</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="nf">exp</span><span class="p">(</span><span class="o">-</span><span class="n">r</span><span class="o">*</span><span class="nb">T</span><span class="p">)</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">pnorm</span><span class="p">(</span><span class="n">d2</span><span class="p">)</span><span class="w">
</span><span class="p">}</span><span class="w">

</span><span class="n">bs_put</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">S</span><span class="p">,</span><span class="w"> </span><span class="n">K</span><span class="p">,</span><span class="w"> </span><span class="n">r</span><span class="p">,</span><span class="w"> </span><span class="n">sigma</span><span class="p">,</span><span class="w"> </span><span class="nb">T</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="n">d1</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="p">(</span><span class="nf">log</span><span class="p">(</span><span class="n">S</span><span class="o">/</span><span class="n">K</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="p">(</span><span class="n">r</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="m">0.5</span><span class="o">*</span><span class="n">sigma</span><span class="o">^</span><span class="m">2</span><span class="p">)</span><span class="o">*</span><span class="nb">T</span><span class="p">)</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="p">(</span><span class="n">sigma</span><span class="o">*</span><span class="nf">sqrt</span><span class="p">(</span><span class="nb">T</span><span class="p">))</span><span class="w">
  </span><span class="n">d2</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">d1</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">sigma</span><span class="o">*</span><span class="nf">sqrt</span><span class="p">(</span><span class="nb">T</span><span class="p">)</span><span class="w">
  </span><span class="n">K</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="nf">exp</span><span class="p">(</span><span class="o">-</span><span class="n">r</span><span class="o">*</span><span class="nb">T</span><span class="p">)</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">pnorm</span><span class="p">(</span><span class="o">-</span><span class="n">d2</span><span class="p">)</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">S</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">pnorm</span><span class="p">(</span><span class="o">-</span><span class="n">d1</span><span class="p">)</span><span class="w">
</span><span class="p">}</span><span class="w">

</span><span class="n">bsc</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">sapply</span><span class="p">(</span><span class="n">strikes</span><span class="p">,</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">K</span><span class="p">)</span><span class="w"> </span><span class="n">bs_call</span><span class="p">(</span><span class="n">S0</span><span class="p">,</span><span class="w"> </span><span class="n">K</span><span class="p">,</span><span class="w"> </span><span class="n">r</span><span class="p">,</span><span class="w"> </span><span class="n">sigma</span><span class="p">,</span><span class="w"> </span><span class="n">T_years</span><span class="p">))</span><span class="w">
</span><span class="n">bsp</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">sapply</span><span class="p">(</span><span class="n">strikes</span><span class="p">,</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">K</span><span class="p">)</span><span class="w"> </span><span class="n">bs_put</span><span class="p">(</span><span class="n">S0</span><span class="p">,</span><span class="w">  </span><span class="n">K</span><span class="p">,</span><span class="w"> </span><span class="n">r</span><span class="p">,</span><span class="w"> </span><span class="n">sigma</span><span class="p">,</span><span class="w"> </span><span class="n">T_years</span><span class="p">))</span><span class="w">

</span><span class="c1"># -- Put-call parity check (MC) -----------------------------------------------</span><span class="w">
</span><span class="c1"># C - P = S0 - K*exp(-rT)  (forward parity)</span><span class="w">
</span><span class="n">pcp_mc</span><span class="w">  </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">mc_call</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">mc_put</span><span class="w">
</span><span class="n">pcp_th</span><span class="w">  </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">S0</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">strikes</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="nf">exp</span><span class="p">(</span><span class="o">-</span><span class="n">r</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">T_years</span><span class="p">)</span><span class="w">
</span><span class="n">pcp_err</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">abs</span><span class="p">(</span><span class="n">pcp_mc</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">pcp_th</span><span class="p">)</span><span class="w">

</span><span class="c1"># -- Summary table ------------------------------------------------------------</span><span class="w">
</span><span class="n">results</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">data.frame</span><span class="p">(</span><span class="w">
  </span><span class="n">K</span><span class="w">          </span><span class="o">=</span><span class="w"> </span><span class="n">strikes</span><span class="p">,</span><span class="w">
  </span><span class="n">BS_call</span><span class="w">    </span><span class="o">=</span><span class="w"> </span><span class="nf">round</span><span class="p">(</span><span class="n">bsc</span><span class="p">,</span><span class="w"> </span><span class="m">4</span><span class="p">),</span><span class="w">
  </span><span class="n">MC_call</span><span class="w">    </span><span class="o">=</span><span class="w"> </span><span class="nf">round</span><span class="p">(</span><span class="n">mc_call</span><span class="p">,</span><span class="w"> </span><span class="m">4</span><span class="p">),</span><span class="w">
  </span><span class="n">err_call</span><span class="w">   </span><span class="o">=</span><span class="w"> </span><span class="nf">round</span><span class="p">(</span><span class="nf">abs</span><span class="p">(</span><span class="n">mc_call</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">bsc</span><span class="p">),</span><span class="w"> </span><span class="m">4</span><span class="p">),</span><span class="w">
  </span><span class="n">BS_put</span><span class="w">     </span><span class="o">=</span><span class="w"> </span><span class="nf">round</span><span class="p">(</span><span class="n">bsp</span><span class="p">,</span><span class="w"> </span><span class="m">4</span><span class="p">),</span><span class="w">
  </span><span class="n">MC_put</span><span class="w">     </span><span class="o">=</span><span class="w"> </span><span class="nf">round</span><span class="p">(</span><span class="n">mc_put</span><span class="p">,</span><span class="w"> </span><span class="m">4</span><span class="p">),</span><span class="w">
  </span><span class="n">err_put</span><span class="w">    </span><span class="o">=</span><span class="w"> </span><span class="nf">round</span><span class="p">(</span><span class="nf">abs</span><span class="p">(</span><span class="n">mc_put</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">bsp</span><span class="p">),</span><span class="w"> </span><span class="m">4</span><span class="p">),</span><span class="w">
  </span><span class="n">PCP_error</span><span class="w">  </span><span class="o">=</span><span class="w"> </span><span class="nf">round</span><span class="p">(</span><span class="n">pcp_err</span><span class="p">,</span><span class="w"> </span><span class="m">4</span><span class="p">)</span><span class="w">
</span><span class="p">)</span><span class="w">

</span><span class="n">cat</span><span class="p">(</span><span class="s2">"\n--- European option prices: MC vs Black-Scholes ---\n"</span><span class="p">)</span><span class="w">
</span><span class="n">print</span><span class="p">(</span><span class="n">results</span><span class="p">,</span><span class="w"> </span><span class="n">row.names</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">FALSE</span><span class="p">)</span><span class="w">

</span><span class="c1"># -- Plot ---------------------------------------------------------------------</span><span class="w">
</span><span class="n">par</span><span class="p">(</span><span class="n">mfrow</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="m">1</span><span class="p">,</span><span class="w"> </span><span class="m">2</span><span class="p">))</span><span class="w">

</span><span class="n">plot</span><span class="p">(</span><span class="n">strikes</span><span class="p">,</span><span class="w"> </span><span class="n">bsc</span><span class="p">,</span><span class="w"> </span><span class="n">type</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"b"</span><span class="p">,</span><span class="w"> </span><span class="n">pch</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">16</span><span class="p">,</span><span class="w"> </span><span class="n">col</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"steelblue"</span><span class="p">,</span><span class="w">
     </span><span class="n">xlab</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Strike"</span><span class="p">,</span><span class="w"> </span><span class="n">ylab</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Price"</span><span class="p">,</span><span class="w"> </span><span class="n">main</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"European call"</span><span class="p">)</span><span class="w">
</span><span class="n">lines</span><span class="p">(</span><span class="n">strikes</span><span class="p">,</span><span class="w"> </span><span class="n">mc_call</span><span class="p">,</span><span class="w"> </span><span class="n">type</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"b"</span><span class="p">,</span><span class="w"> </span><span class="n">pch</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">17</span><span class="p">,</span><span class="w"> </span><span class="n">col</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"coral"</span><span class="p">,</span><span class="w"> </span><span class="n">lty</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">2</span><span class="p">)</span><span class="w">
</span><span class="n">legend</span><span class="p">(</span><span class="s2">"topright"</span><span class="p">,</span><span class="w"> </span><span class="n">legend</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="s2">"Black-Scholes"</span><span class="p">,</span><span class="w"> </span><span class="s2">"Monte Carlo"</span><span class="p">),</span><span class="w">
       </span><span class="n">col</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="s2">"steelblue"</span><span class="p">,</span><span class="w"> </span><span class="s2">"coral"</span><span class="p">),</span><span class="w"> </span><span class="n">pch</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="m">16</span><span class="p">,</span><span class="w"> </span><span class="m">17</span><span class="p">),</span><span class="w"> </span><span class="n">lty</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="m">1</span><span class="p">,</span><span class="w"> </span><span class="m">2</span><span class="p">))</span><span class="w">

</span><span class="n">plot</span><span class="p">(</span><span class="n">strikes</span><span class="p">,</span><span class="w"> </span><span class="n">bsp</span><span class="p">,</span><span class="w"> </span><span class="n">type</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"b"</span><span class="p">,</span><span class="w"> </span><span class="n">pch</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">16</span><span class="p">,</span><span class="w"> </span><span class="n">col</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"steelblue"</span><span class="p">,</span><span class="w">
     </span><span class="n">xlab</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Strike"</span><span class="p">,</span><span class="w"> </span><span class="n">ylab</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Price"</span><span class="p">,</span><span class="w"> </span><span class="n">main</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"European put"</span><span class="p">)</span><span class="w">
</span><span class="n">lines</span><span class="p">(</span><span class="n">strikes</span><span class="p">,</span><span class="w"> </span><span class="n">mc_put</span><span class="p">,</span><span class="w"> </span><span class="n">type</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"b"</span><span class="p">,</span><span class="w"> </span><span class="n">pch</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">17</span><span class="p">,</span><span class="w"> </span><span class="n">col</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"coral"</span><span class="p">,</span><span class="w"> </span><span class="n">lty</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">2</span><span class="p">)</span><span class="w">
</span><span class="n">legend</span><span class="p">(</span><span class="s2">"topleft"</span><span class="p">,</span><span class="w"> </span><span class="n">legend</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="s2">"Black-Scholes"</span><span class="p">,</span><span class="w"> </span><span class="s2">"Monte Carlo"</span><span class="p">),</span><span class="w">
       </span><span class="n">col</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="s2">"steelblue"</span><span class="p">,</span><span class="w"> </span><span class="s2">"coral"</span><span class="p">),</span><span class="w"> </span><span class="n">pch</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="m">16</span><span class="p">,</span><span class="w"> </span><span class="m">17</span><span class="p">),</span><span class="w"> </span><span class="n">lty</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="m">1</span><span class="p">,</span><span class="w"> </span><span class="m">2</span><span class="p">))</span><span class="w">

</span><span class="n">par</span><span class="p">(</span><span class="n">mfrow</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="m">1</span><span class="p">,</span><span class="w"> </span><span class="m">1</span><span class="p">))</span><span class="w">
</span></code></pre></div></div>

<p><img src="/images/2026-03-16/2026-03-16-image1.png" alt="image-title-here" class="img-responsive" /></p>]]></content><author><name></name></author><category term="R" /><summary type="html"><![CDATA[Option pricing using time series models as market price of risk and bootstrap resampling]]></summary></entry><entry><title type="html">Explaining Time-Series Forecasts with Exact Shapley Values (ahead::dynrmf with external regressors applied to scenarios)</title><link href="https://thierrymoudiki.github.io/blog/2026/03/08/r/exact-shapley-dynrmf" rel="alternate" type="text/html" title="Explaining Time-Series Forecasts with Exact Shapley Values (ahead::dynrmf with external regressors applied to scenarios)" /><published>2026-03-08T00:00:00+00:00</published><updated>2026-03-08T00:00:00+00:00</updated><id>https://thierrymoudiki.github.io/blog/2026/03/08/r/exact-shapley-dynrmf</id><content type="html" xml:base="https://thierrymoudiki.github.io/blog/2026/03/08/r/exact-shapley-dynrmf"><![CDATA[<p>Shapley values constitute a widely adopted way to attribute the contribution of each feature (explanatory variable) to the prediction of a model. Mostly used in supervised learning, this post illustrates an example of how to use them to explain time-series forecasts, with exact Shapley values, and based on the <code class="language-plaintext highlighter-rouge">ahead::dynrmf</code> model with external regressors.</p>

<p>The code below uses the <code class="language-plaintext highlighter-rouge">ahead</code> package to compute exact Shapley values for a time-series forecast. It uses the <code class="language-plaintext highlighter-rouge">ahead::dynrmf_shap</code> function to compute the Shapley values and the <code class="language-plaintext highlighter-rouge">ahead::plot_dynrmf_shap_waterfall</code> function to plot them.</p>

<p>First, install the package:</p>

<div class="language-R highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">devtools</span><span class="o">::</span><span class="n">install_github</span><span class="p">(</span><span class="s2">"Techtonique/ahead"</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p>Then, run the following code (applies Shapley values to the <code class="language-plaintext highlighter-rouge">dynrmf</code> model, for different scenarios). I use the <code class="language-plaintext highlighter-rouge">uschange</code> dataset (quarterly changes in US macroeconomic variables) from the <code class="language-plaintext highlighter-rouge">fpp2</code> package. The target time series variable is <code class="language-plaintext highlighter-rouge">Consumption</code>; the regressors are <code class="language-plaintext highlighter-rouge">Income</code>, <code class="language-plaintext highlighter-rouge">Savings</code>, and <code class="language-plaintext highlighter-rouge">Unemployment</code> (scaled).</p>

<div class="language-R highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">library</span><span class="p">(</span><span class="n">fpp2</span><span class="p">);</span><span class="w"> </span><span class="n">library</span><span class="p">(</span><span class="n">ahead</span><span class="p">);</span><span class="w"> </span><span class="n">library</span><span class="p">(</span><span class="n">e1071</span><span class="p">);</span><span class="w"> </span><span class="n">library</span><span class="p">(</span><span class="n">misc</span><span class="p">)</span><span class="w">
</span><span class="n">library</span><span class="p">(</span><span class="n">ggplot2</span><span class="p">);</span><span class="w"> </span><span class="n">library</span><span class="p">(</span><span class="n">patchwork</span><span class="p">)</span><span class="w">

</span><span class="n">y</span><span class="w">       </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">fpp2</span><span class="o">::</span><span class="n">uschange</span><span class="p">[,</span><span class="w"> </span><span class="s2">"Consumption"</span><span class="p">]</span><span class="w">
</span><span class="n">xreg</span><span class="w">    </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">scale</span><span class="p">(</span><span class="n">fpp2</span><span class="o">::</span><span class="n">uschange</span><span class="p">[,</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="s2">"Income"</span><span class="p">,</span><span class="w"> </span><span class="s2">"Savings"</span><span class="p">,</span><span class="w"> </span><span class="s2">"Unemployment"</span><span class="p">)])</span><span class="w">
</span><span class="n">split</span><span class="w">   </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">misc</span><span class="o">::</span><span class="n">splitts</span><span class="p">(</span><span class="n">y</span><span class="p">,</span><span class="w"> </span><span class="n">split_prob</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.9</span><span class="p">)</span><span class="w">
</span><span class="n">xreg_train</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">window</span><span class="p">(</span><span class="n">xreg</span><span class="p">,</span><span class="w"> </span><span class="n">start</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">start</span><span class="p">(</span><span class="n">split</span><span class="o">$</span><span class="n">training</span><span class="p">),</span><span class="w"> </span><span class="n">end</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">end</span><span class="p">(</span><span class="n">split</span><span class="o">$</span><span class="n">training</span><span class="p">))</span><span class="w">
</span><span class="n">xreg_test</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">window</span><span class="p">(</span><span class="n">xreg</span><span class="p">,</span><span class="w"> </span><span class="n">start</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">start</span><span class="p">(</span><span class="n">split</span><span class="o">$</span><span class="n">testing</span><span class="p">),</span><span class="w">  </span><span class="n">end</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">end</span><span class="p">(</span><span class="n">split</span><span class="o">$</span><span class="n">testing</span><span class="p">))</span><span class="w">

</span><span class="n">shap</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">ahead</span><span class="o">::</span><span class="n">dynrmf_shap</span><span class="p">(</span><span class="w">
  </span><span class="n">y</span><span class="w">            </span><span class="o">=</span><span class="w"> </span><span class="n">split</span><span class="o">$</span><span class="n">training</span><span class="p">,</span><span class="w">
  </span><span class="n">xreg_fit</span><span class="w">     </span><span class="o">=</span><span class="w"> </span><span class="n">xreg_train</span><span class="p">,</span><span class="w">
  </span><span class="n">xreg_predict</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">xreg_test</span><span class="p">,</span><span class="w">
  </span><span class="n">fit_func</span><span class="w">     </span><span class="o">=</span><span class="w"> </span><span class="n">e1071</span><span class="o">::</span><span class="n">svm</span><span class="w">
</span><span class="p">)</span><span class="w">

</span><span class="n">p1</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">ahead</span><span class="o">::</span><span class="n">plot_dynrmf_shap_waterfall</span><span class="p">(</span><span class="n">shap</span><span class="p">,</span><span class="w"> </span><span class="n">title</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Baseline scenario"</span><span class="p">)</span><span class="w">

</span><span class="n">xreg_pess</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">xreg_test</span><span class="w">
</span><span class="n">xreg_pess</span><span class="p">[,</span><span class="s2">"Income"</span><span class="p">]</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">-1</span><span class="p">;</span><span class="w">
</span><span class="n">xreg_pess</span><span class="p">[,</span><span class="s2">"Savings"</span><span class="p">]</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">-0.5</span><span class="w">

</span><span class="n">shap_pess</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">dynrmf_shap</span><span class="p">(</span><span class="w">
  </span><span class="n">y</span><span class="w">            </span><span class="o">=</span><span class="w"> </span><span class="n">split</span><span class="o">$</span><span class="n">training</span><span class="p">,</span><span class="w">
  </span><span class="n">xreg_fit</span><span class="w">     </span><span class="o">=</span><span class="w"> </span><span class="n">xreg_train</span><span class="p">,</span><span class="w">
  </span><span class="n">xreg_predict</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">xreg_pess</span><span class="p">,</span><span class="w">
  </span><span class="n">fit_func</span><span class="w">     </span><span class="o">=</span><span class="w"> </span><span class="n">e1071</span><span class="o">::</span><span class="n">svm</span><span class="w">
</span><span class="p">)</span><span class="w">

</span><span class="n">p2</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">ahead</span><span class="o">::</span><span class="n">plot_dynrmf_shap_waterfall</span><span class="p">(</span><span class="n">shap_pess</span><span class="p">,</span><span class="w"> </span><span class="n">title</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Pessimistic scenario"</span><span class="p">)</span><span class="w">

</span><span class="n">xreg_opt</span><span class="w">  </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">xreg_test</span><span class="w">
</span><span class="n">xreg_opt</span><span class="p">[,</span><span class="s2">"Income"</span><span class="p">]</span><span class="w">  </span><span class="o">&lt;-</span><span class="w">  </span><span class="m">2</span><span class="p">;</span><span class="w">
</span><span class="n">xreg_opt</span><span class="p">[,</span><span class="s2">"Savings"</span><span class="p">]</span><span class="w">  </span><span class="o">&lt;-</span><span class="w">  </span><span class="m">0.5</span><span class="w">

</span><span class="n">shap_opt</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">dynrmf_shap</span><span class="p">(</span><span class="w">
  </span><span class="n">y</span><span class="w">            </span><span class="o">=</span><span class="w"> </span><span class="n">split</span><span class="o">$</span><span class="n">training</span><span class="p">,</span><span class="w">
  </span><span class="n">xreg_fit</span><span class="w">     </span><span class="o">=</span><span class="w"> </span><span class="n">xreg_train</span><span class="p">,</span><span class="w">
  </span><span class="n">xreg_predict</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">xreg_opt</span><span class="p">,</span><span class="w">
  </span><span class="n">fit_func</span><span class="w">     </span><span class="o">=</span><span class="w"> </span><span class="n">e1071</span><span class="o">::</span><span class="n">svm</span><span class="w">
</span><span class="p">)</span><span class="w">

</span><span class="n">p3</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">ahead</span><span class="o">::</span><span class="n">plot_dynrmf_shap_waterfall</span><span class="p">(</span><span class="n">shap_opt</span><span class="p">,</span><span class="w"> </span><span class="n">title</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Optimistic scenario"</span><span class="p">)</span><span class="w">

</span><span class="n">xreg_ovr</span><span class="w">  </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">xreg_test</span><span class="w">
</span><span class="n">xreg_ovr</span><span class="p">[,</span><span class="s2">"Income"</span><span class="p">]</span><span class="w">  </span><span class="o">&lt;-</span><span class="w">  </span><span class="m">2.5</span><span class="p">;</span><span class="w">
</span><span class="n">xreg_ovr</span><span class="p">[,</span><span class="s2">"Savings"</span><span class="p">]</span><span class="w"> </span><span class="o">&lt;-</span><span class="w">  </span><span class="m">0.75</span><span class="w">

</span><span class="n">shap_ovr</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">ahead</span><span class="o">::</span><span class="n">dynrmf_shap</span><span class="p">(</span><span class="w">
  </span><span class="n">y</span><span class="w">            </span><span class="o">=</span><span class="w"> </span><span class="n">split</span><span class="o">$</span><span class="n">training</span><span class="p">,</span><span class="w">
  </span><span class="n">xreg_fit</span><span class="w">     </span><span class="o">=</span><span class="w"> </span><span class="n">xreg_train</span><span class="p">,</span><span class="w">
  </span><span class="n">xreg_predict</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">xreg_ovr</span><span class="p">,</span><span class="w">
  </span><span class="n">fit_func</span><span class="w">     </span><span class="o">=</span><span class="w"> </span><span class="n">e1071</span><span class="o">::</span><span class="n">svm</span><span class="w">
</span><span class="p">)</span><span class="w">

</span><span class="n">p4</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">plot_dynrmf_shap_waterfall</span><span class="p">(</span><span class="n">shap_ovr</span><span class="p">,</span><span class="w"> </span><span class="n">title</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Overly optimistic scenario"</span><span class="p">)</span><span class="w">

</span><span class="p">(</span><span class="n">p1</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">p2</span><span class="p">)</span><span class="o">/</span><span class="p">(</span><span class="n">p3</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">p4</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p><img src="/images/2026-03-08/2026-03-08-image1.png" alt="image-title-here" class="img-responsive" /></p>

<p>One check, which is always a good practice when using Shapley values, is to see if the sum of the Shapley values equals the difference between the prediction and the baseline forecast (the model forecast when every regressor is replaced by its training set column mean). It’s the case on the plots above.</p>

<p>It’s worth mentioning that exact Shapley values can be computed in this context because there are only a few external regressors. This remains feasible for a small number of regressors (less than 15, which, again, in this context, is not absurd to consider).</p>]]></content><author><name></name></author><category term="R" /><summary type="html"><![CDATA[Explaining Time-Series Forecasts with Exact Shapley Values (ahead::dynrmf with external regressors applied to macroeconomic scenarios)]]></summary></entry><entry><title type="html">My Presentation at Risk 2026: Lightweight Transfer Learning for Financial Forecasting</title><link href="https://thierrymoudiki.github.io/blog/2026/03/01/r/My-Presentation-Risk-2026-Lightweight-Transfer-Learning" rel="alternate" type="text/html" title="My Presentation at Risk 2026: Lightweight Transfer Learning for Financial Forecasting" /><published>2026-03-01T00:00:00+00:00</published><updated>2026-03-01T00:00:00+00:00</updated><id>https://thierrymoudiki.github.io/blog/2026/03/01/r/My-Presentation-Risk-2026-Lightweight-Transfer-Learning</id><content type="html" xml:base="https://thierrymoudiki.github.io/blog/2026/03/01/r/My-Presentation-Risk-2026-Lightweight-Transfer-Learning"><![CDATA[<p>At the <a href="https://rconsortium.github.io/Risk_website/Abstracts.html#lightweight-transfer-learning-for-financial-forecasting-using-quasi-randomized-networks">Risk 2026</a> conference organized by the R consortium, I presented “Lightweight Transfer Learning for Financial Forecasting Using Quasi-Randomized Networks”. Here are the slides:</p>

<p><a href="https://github.com/thierrymoudiki/presentations/blob/main/2026-02-19-Risk-2026-transfer_learning_stock_returns.pdf">https://github.com/thierrymoudiki/presentations/blob/main/2026-02-19-Risk-2026-transfer_learning_stock_returns.pdf</a></p>

<p>And the R code can be found <a href="https://github.com/thierrymoudiki/2025-09-05-transfer-learning-ridge2f">here</a>.</p>

<p>My previous posts on the same subject were:</p>

<ul>
  <li><a href="https://thierrymoudiki.github.io/blog/2025/09/08/r/python/pretraining-ridge2f">https://thierrymoudiki.github.io/blog/2025/09/08/r/python/pretraining-ridge2f</a></li>
  <li><a href="https://thierrymoudiki.github.io/blog/2025/09/09/r/python/pretraining-ridge2f-part2">https://thierrymoudiki.github.io/blog/2025/09/09/r/python/pretraining-ridge2f-part2</a></li>
</ul>

<p><img src="/images/2026-03-01/2026-03-01-image1.png" alt="image-title-here" class="img-responsive" /></p>]]></content><author><name></name></author><category term="R" /><summary type="html"><![CDATA[My Presentation at Risk 2026: Lightweight Transfer Learning for Financial Forecasting]]></summary></entry><entry><title type="html">nnetsauce with and without jax for GPU acceleration</title><link href="https://thierrymoudiki.github.io/blog/2026/02/23/r/python/nnetsauce-without-jax" rel="alternate" type="text/html" title="nnetsauce with and without jax for GPU acceleration" /><published>2026-02-23T00:00:00+00:00</published><updated>2026-02-23T00:00:00+00:00</updated><id>https://thierrymoudiki.github.io/blog/2026/02/23/r/python/nnetsauce-without-jax</id><content type="html" xml:base="https://thierrymoudiki.github.io/blog/2026/02/23/r/python/nnetsauce-without-jax"><![CDATA[<p>In the new version (<code class="language-plaintext highlighter-rouge">0.51.2</code>) of nnetsauce (for Python, but also <a href="https://thierrymoudiki.github.io/blog/2025/12/17/r/python/new-nnetsauce-R-uv">for R</a>), available on PyPI and for conda, I removed jax and jaxlib (for GPU) from the default version, because jaxlib is heavy.</p>

<p>It means that if you want to use GPUs with nnetsauce (as in <a href="https://www.researchgate.net/publication/382589729_Probabilistic_Forecasting_with_nnetsauce_using_Density_Estimation_Bayesian_inference_Conformal_prediction_and_Vine_copulas">https://www.researchgate.net/publication/382589729_Probabilistic_Forecasting_with_nnetsauce_using_Density_Estimation_Bayesian_inference_Conformal_prediction_and_Vine_copulas</a>), you’d want to explicitly install jax:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pip install nnetsauce[jax]
</code></pre></div></div>

<p>or</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>uv pip install nnetsauce[jax]
</code></pre></div></div>

<p>or</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>conda install -c conda-forge nnetsauce jax jaxlib
</code></pre></div></div>]]></content><author><name></name></author><category term="R" /><category term="Python" /><summary type="html"><![CDATA[How to install nnetsauce with and without jax for GPU acceleration]]></summary></entry><entry><title type="html">Understanding Boosted Configuration Networks (combined neural networks and boosting): An Intuitive Guide Through Their Hyperparameters</title><link href="https://thierrymoudiki.github.io/blog/2026/02/16/r/python/bcn-explained" rel="alternate" type="text/html" title="Understanding Boosted Configuration Networks (combined neural networks and boosting): An Intuitive Guide Through Their Hyperparameters" /><published>2026-02-16T00:00:00+00:00</published><updated>2026-02-16T00:00:00+00:00</updated><id>https://thierrymoudiki.github.io/blog/2026/02/16/r/python/bcn-explained</id><content type="html" xml:base="https://thierrymoudiki.github.io/blog/2026/02/16/r/python/bcn-explained"><![CDATA[<p><strong>Disclaimer:</strong> This post was written with the help of LLMs, based on:</p>

<ul>
  <li><a href="https://thierrymoudiki.github.io/blog/2022/07/21/r/misc/boosted-configuration-networks">https://thierrymoudiki.github.io/blog/2022/07/21/r/misc/boosted-configuration-networks</a></li>
  <li><a href="https://www.researchgate.net/publication/332291211_Forecasting_multivariate_time_series_with_boosted_configuration_networks">https://www.researchgate.net/publication/332291211_Forecasting_multivariate_time_series_with_boosted_configuration_networks</a></li>
  <li><a href="https://docs.techtonique.net/bcn/articles/bcn-intro.html">https://docs.techtonique.net/bcn/articles/bcn-intro.html</a></li>
  <li><a href="https://github.com/Techtonique/bcn_python">https://github.com/Techtonique/bcn_python</a></li>
</ul>

<p>Potential remaining errors are mine.</p>

<hr />

<p>What if you could have a model that:</p>
<ul>
  <li>✅ Captures non-linear patterns like neural networks</li>
  <li>✅ Builds iteratively like gradient boosting</li>
  <li>✅ Provides built-in interpretability through its additive structure</li>
  <li>✅ Works well on regression, classitication, time series</li>
</ul>

<p>That’s <strong>Boosted Configuration Networks (BCN)</strong>.</p>

<p><strong>Where BCN fits:</strong> BCN sits between Neural Additive Models (NAMs) and gradient boosting—combining neural flexibility with boosting’s greedy refinement. It’s particularly effective for:</p>

<ul>
  <li>Medium-sized tabular datasets (100s to 10,000s of rows)</li>
  <li>Multivariate prediction tasks (multiple outputs that share structure)</li>
  <li>Problems requiring both accuracy and interpretability</li>
  <li>Time series forecasting with multiple related series</li>
</ul>

<p>In this post, I’ll explain BCN’s intuition by walking through its hyperparameters. Each parameter reveals something fundamental about how the algorithm works.</p>

<hr />

<h2 id="the-core-idea-building-smart-weak-learners">The Core Idea: Building Smart Weak Learners</h2>

<p>BCN asks a simple question at each iteration:</p>

<blockquote>
  <p>“What’s the <em>best</em> artificial neural network feature I can add right now to explain what I haven’t captured yet?”</p>
</blockquote>

<p>Let’s break down this sentence:</p>

<h3 id="1-artificial-neural-network-feature">1. artificial <strong>“Neural network feature”</strong></h3>

<p>At each iteration L, a BCN creates a simple single-layer feedforward neural network:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>h_L = activation(w_L^T · x)
</code></pre></div></div>
<p>This is just: multiply features by weights, then apply an activation function (tanh or sigmoid; bounded).</p>

<h3 id="2-best">2. <strong><em>Best</em></strong></h3>

<p>BCN finds weights <code class="language-plaintext highlighter-rouge">w_L</code> that <strong>maximize how much this feature explains the residuals</strong>.</p>

<p>Specifically, it finds the artificial neural network whose output has the <strong>largest regression coefficient</strong> when predicting the residuals. This is captured in the ξ (xi) criterion:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ξ = ν(2-ν)·β²_L - penalty
</code></pre></div></div>

<p>where <code class="language-plaintext highlighter-rouge">β_L</code> is the least-squares coefficient from regressing residuals on <code class="language-plaintext highlighter-rouge">h_L</code>.</p>

<h3 id="3-what-i-havent-captured-yet">3. <strong>“What I haven’t captured yet”</strong></h3>

<p>Like all boosting methods, BCN works on <strong>residuals</strong> - the gap between current predictions and truth. Each iteration “carves away” at the error.</p>

<h3 id="4-add">4. <strong>“Add”</strong></h3>

<p>Once we find the <em>best</em> <code class="language-plaintext highlighter-rouge">h_L</code>, we add it to our ensemble:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>new prediction = old prediction + ν · β_L · h_L
</code></pre></div></div>

<p><strong>Visual mental model:</strong> Imagine starting with the mean prediction (flat surface). Each iteration adds a “bump” (artificial neural network feature) where the residuals are largest, gradually sculpting a complex prediction surface.</p>

<p>Now let’s see how the hyperparameters control this process.</p>

<hr />

<h2 id="hyperparameter-priority-the-big-three">Hyperparameter Priority: The Big Three</h2>

<p>Before diving deep, here’s how parameters rank by impact:</p>

<p><strong>Tier 1 - Critical (tune these first):</strong></p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">B</code> (iterations): Model complexity</li>
  <li><code class="language-plaintext highlighter-rouge">nu</code> (learning rate): Step size and stability</li>
  <li><code class="language-plaintext highlighter-rouge">lam</code> (weight bounds): Feature complexity</li>
</ul>

<p><strong>Tier 2 - Regularization (tune for robustness):</strong></p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">r</code> (convergence rate, most of the times 0.99, 0.999, 0.9999, etc.): Adaptive quality control</li>
  <li><code class="language-plaintext highlighter-rouge">col_sample</code> (feature sampling): Regularization via randomness</li>
</ul>

<p><strong>Tier 3 - Technical (usually keep defaults):</strong></p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">type_optim</code> (optimizer): Computational trade-offs</li>
  <li><code class="language-plaintext highlighter-rouge">activation</code> (nonlinearity): Usually tanh because bounded</li>
  <li><code class="language-plaintext highlighter-rouge">hidden_layer_bias</code>: Usually TRUE</li>
  <li><code class="language-plaintext highlighter-rouge">tol</code> (tolerance): Early stopping</li>
</ul>

<hr />

<h2 id="hyperparameter-1-b-number-of-iterations">Hyperparameter 1: <code class="language-plaintext highlighter-rouge">B</code> (Number of Iterations)</h2>

<p><strong>Default:</strong> No universal default (typically 100-500)</p>

<p><strong>What it controls:</strong> How many weak learners to train</p>

<p><strong>Intuition:</strong> BCN builds your model piece by piece. Each iteration adds one artificial neural network feature that explains some of what you haven’t captured.</p>

<p><strong>Trade-offs:</strong></p>

<ul>
  <li><strong>Small B (10-50):</strong>
    <ul>
      <li>✅ Fast training</li>
      <li>✅ Less risk of overfitting</li>
      <li>❌ May underfit complex relationships</li>
    </ul>
  </li>
  <li><strong>Large B (100-1000):</strong>
    <ul>
      <li>✅ Can capture subtle patterns</li>
      <li>✅ Better accuracy on complex tasks</li>
      <li>❌ Slower training</li>
      <li>❌ Risk of overfitting without other regularization</li>
    </ul>
  </li>
</ul>

<p><strong>Rule of thumb:</strong> Start with B=100. If using early stopping (<code class="language-plaintext highlighter-rouge">tol</code> &gt; 0), set B high (500-1000) and let the algorithm stop when improvement plateaus.</p>

<p><strong>What’s happening internally:</strong>
Each iteration finds weights <code class="language-plaintext highlighter-rouge">w_L</code> that maximize:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ξ_L = ν(2-ν) · β²_L - penalty
</code></pre></div></div>
<p>where β²_L measures how much the neural network feature correlates with residuals.</p>

<hr />

<h2 id="hyperparameter-2-nu-learning-rate">Hyperparameter 2: <code class="language-plaintext highlighter-rouge">nu</code> (Learning Rate)</h2>

<p><strong>Default:</strong> 0.1 (conservative)<br />
<strong>Typical range:</strong> 0.1-0.8<br />
<strong>Sweet spot:</strong> 0.3-0.5</p>

<p><strong>What it controls:</strong> How aggressively to use each weak learner</p>

<p><strong>Intuition:</strong> Even if you find a great neural network feature, you might not want to use it at full strength. The learning rate controls the step size.</p>

<p>When BCN finds a good feature h_L with coefficient β_L, it updates predictions by:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>prediction += nu · β_L · h_L
</code></pre></div></div>

<p><strong>Trade-offs:</strong></p>
<ul>
  <li><strong>Small ν (0.1-0.3):</strong>
    <ul>
      <li>✅ More stable training</li>
      <li>✅ Better generalization (smooths out noise)</li>
      <li>✅ Less sensitive to individual weak learners</li>
      <li>❌ Need more iterations (larger B)</li>
      <li>❌ Slower convergence</li>
    </ul>
  </li>
  <li><strong>Large ν (0.5-1.0):</strong>
    <ul>
      <li>✅ Faster convergence</li>
      <li>✅ Fewer iterations needed</li>
      <li>❌ Risk of overfitting</li>
      <li>❌ Can be unstable</li>
    </ul>
  </li>
</ul>

<p><strong>Why ν(2-ν) appears in the math:</strong></p>

<p>This factor arises when we want to prove the convergence of residuals’ L2-norm towards 0. It’s <strong>maximized at ν=1</strong> (full gradient step):</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>f(ν) = 2ν - ν² 
f'(ν) = 2 - 2ν = 0  ⟹  ν = 1
</code></pre></div></div>
<p>This ensures stability for ν ∈ (0,2) and explains why ν=1 is the “natural” full step.</p>

<p><strong>Think of it like:</strong></p>
<ul>
  <li>ν=0.1: “I trust each feature a little, build slowly” (like learning rate 0.01 in SGD)</li>
  <li>ν=0.5: “I trust each feature moderately, build steadily”</li>
  <li>ν=1.0: “I trust each feature fully, build quickly” (can be unstable)</li>
</ul>

<hr />

<h2 id="hyperparameter-3-lam-λ---weight-bounds">Hyperparameter 3: <code class="language-plaintext highlighter-rouge">lam</code> (λ - Weight Bounds)</h2>

<p><strong>Default:</strong> 0.1<br />
<strong>Typical range:</strong> 0.1-100 (often on log scale: 10^0 to 10^2)<br />
<strong>Sweet spot:</strong> 10^(0.5 to 1.0) ≈ 3-10</p>

<p><strong>What it controls:</strong> How large the neural network weights can be</p>

<p><strong>Intuition:</strong> This constrains the weights <code class="language-plaintext highlighter-rouge">w_L</code> at each iteration to the range [-λ, λ]. It’s a form of regularization through box constraints.</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Tight constraints: simpler features</span><span class="w">
</span><span class="n">fit_simple</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">bcn</span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="p">,</span><span class="w"> </span><span class="n">lam</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.5</span><span class="p">)</span><span class="w">

</span><span class="c1"># Loose constraints: more complex features</span><span class="w">
</span><span class="n">fit_complex</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">bcn</span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="p">,</span><span class="w"> </span><span class="n">lam</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">10.0</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p><strong>Why this matters:</strong></p>

<p><strong>Small λ (0.1-1.0):</strong></p>

<ul>
  <li>Neural network features are “gentle” (bounded outputs)</li>
  <li>Less risk of overfitting</li>
  <li>May miss complex interactions</li>
  <li>✅ Use for: Small datasets, high interpretability needs</li>
</ul>

<p><strong>Large λ (5-100):</strong></p>

<ul>
  <li>Neural network features can be more “extreme”</li>
  <li>Can capture stronger non-linearities</li>
  <li>Risk of overfitting if not balanced with other regularization</li>
  <li>✅ Use for: Complex patterns, large datasets</li>
</ul>

<p><strong>What’s happening mathematically:</strong></p>

<p>At each iteration, we solve:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>maximize ξ(w_L)
subject to: -λ ≤ w_L,j ≤ λ for all features j
</code></pre></div></div>

<p>This is a <strong>constrained optimization</strong> - we’re finding the <em>best</em> weights within a box.</p>

<p><strong>Think of it like:</strong></p>

<ul>
  <li>Small λ: “Keep the weak learners simple” (like L∞ regularization)</li>
  <li>Large λ: “Allow complex weak learners”</li>
</ul>

<p><strong>Note on consistency:</strong> In the code, this parameter is <code class="language-plaintext highlighter-rouge">lam</code> (avoiding the Greek letter for R compatibility).</p>

<hr />

<h2 id="hyperparameter-4-r-convergence-rate">Hyperparameter 4: <code class="language-plaintext highlighter-rouge">r</code> (Convergence Rate)</h2>

<p><strong>Default:</strong> 0.3<br />
<strong>Typical range:</strong> 0.3-0.99<br />
<strong>Sweet spot:</strong> 0.9-0.99999</p>

<p><strong>What it controls:</strong> How the acceptance threshold changes over iterations</p>

<p><strong>Intuition:</strong> This is the <strong>most subtle</strong> hyperparameter. It controls how picky BCN is about accepting new weak learners, and this pickiness <em>decreases</em> as training progresses. Think of <code class="language-plaintext highlighter-rouge">r</code> as the “patience” or “quality control” officer: high r means “Only the best features get through the door early on.”</p>

<p><strong>The acceptance criterion:</strong></p>

<p>BCN only accepts a weak learner if:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ξ_L = ν(2-ν)·β²_L - [1 - r - (1-r)/(L+1)]·||residuals||² ≥ 0
</code></pre></div></div>

<p>The penalty term <code class="language-plaintext highlighter-rouge">[1 - r - (1-r)/(L+1)]</code> <strong>decreases</strong> as L increases:</p>

<table>
  <thead>
    <tr>
      <th>Iteration L</th>
      <th>r = 0.95</th>
      <th>r = 0.70</th>
      <th>r = 0.50</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>L = 1</td>
      <td>0.075</td>
      <td>0.45</td>
      <td>0.75</td>
    </tr>
    <tr>
      <td>L = 10</td>
      <td>0.055</td>
      <td>0.33</td>
      <td>0.55</td>
    </tr>
    <tr>
      <td>L = 100</td>
      <td>0.050</td>
      <td>0.30</td>
      <td>0.50</td>
    </tr>
    <tr>
      <td>L → ∞</td>
      <td>0.050</td>
      <td>0.30</td>
      <td>0.50</td>
    </tr>
  </tbody>
</table>

<p><strong>Interpretation:</strong> The penalty starts higher and converges to <code class="language-plaintext highlighter-rouge">(1-r)</code> as training progresses.</p>

<p><strong>Trade-offs:</strong></p>

<p><strong>Large r (0.9-0.99):</strong></p>

<ul>
  <li>Early iterations: very picky (high penalty)</li>
  <li>Later iterations: more permissive</li>
  <li>✅ Prevents premature commitment to poor features</li>
  <li>✅ Allows fine-tuning in later iterations</li>
  <li>✅ Better generalization</li>
  <li>✅ Use for: Production models, complex tasks</li>
</ul>

<p><strong>Small r (0.3-0.7):</strong></p>

<ul>
  <li>Less selective throughout training</li>
  <li>✅ Accepts more weak learners</li>
  <li>✅ Faster initial progress</li>
  <li>❌ May accept noisy features early</li>
  <li>✅ Use for: Quick prototyping, exploratory work</li>
</ul>

<p><strong>The dynamic threshold:</strong></p>

<p>Rearranging the acceptance criterion:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Required R² &gt; [1 - r - (1-r)/(L+1)] / [ν(2-ν)]
</code></pre></div></div>

<p>This creates an <strong>adaptive</strong> selection criterion that evolves during training.</p>

<p><strong>Think of it like:</strong></p>

<ul>
  <li>High r: “Be very careful early on (we have lots of iterations left), but allow refinements later”</li>
  <li>Low r: “Accept good-enough features throughout training”</li>
</ul>

<hr />

<h2 id="hyperparameter-5-col_sample-feature-sampling">Hyperparameter 5: <code class="language-plaintext highlighter-rouge">col_sample</code> (Feature Sampling)</h2>

<p><strong>Default:</strong> 1.0 (no sampling)<br />
<strong>Typical range:</strong> 0.3-1.0<br />
<strong>Sweet spot:</strong> 0.5-0.7 for high-dimensional data</p>

<p><strong>What it controls:</strong> What fraction of features to consider at each iteration</p>

<p><strong>Intuition:</strong> Like Random Forests, BCN can use only a random subset of features at each iteration. This reduces overfitting, adds diversity, and speeds up computation.</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Use all features (no sampling)</span><span class="w">
</span><span class="n">fit_full</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">bcn</span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="p">,</span><span class="w"> </span><span class="n">col_sample</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">1.0</span><span class="p">)</span><span class="w">

</span><span class="c1"># Use 50% of features at each iteration</span><span class="w">
</span><span class="n">fit_sampled</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">bcn</span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="p">,</span><span class="w"> </span><span class="n">col_sample</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.5</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p><strong>How it works:</strong>
At iteration L, randomly sample <code class="language-plaintext highlighter-rouge">col_sample × d</code> features and optimize only over those:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>w_L ∈ R^d_reduced    (instead of R^d)
</code></pre></div></div>

<p>Different features are sampled at each iteration, creating diversity like Random Forests but for neural network features.</p>

<p><strong>Trade-offs:</strong></p>

<p><strong>col_sample = 1.0 (no sampling):</strong></p>

<ul>
  <li>✅ Can use all information</li>
  <li>✅ Potentially better accuracy</li>
  <li>❌ Slower training (larger optimization)</li>
  <li>❌ Higher overfitting risk</li>
  <li>✅ Use for: Small datasets (N &lt; 1000), few features (d &lt; 50)</li>
</ul>

<p><strong>col_sample = 0.3-0.7:</strong></p>

<ul>
  <li>✅ Faster training (smaller optimization)</li>
  <li>✅ Regularization effect (like Random Forests)</li>
  <li>✅ More diverse weak learners</li>
  <li>❌ May miss important feature combinations</li>
  <li>✅ Use for: Large datasets, many features (d &gt; 100)</li>
</ul>

<p><strong>Interaction with B:</strong>
Column sampling as implicit regularization means you may need more iterations:</p>

<hr />

<h2 id="hyperparameter-6-activation-activation-function">Hyperparameter 6: <code class="language-plaintext highlighter-rouge">activation</code> (Activation Function)</h2>

<p><strong>Default:</strong> “tanh”<br />
<strong>Options:</strong> “tanh”, “sigmoid”</p>

<p><strong>What it controls:</strong> The non-linearity in each weak learner</p>

<p><strong>Intuition:</strong> This determines the shape of transformations each neural network can create.</p>

<p><strong>Characteristics:</strong></p>

<p><strong>tanh (hyperbolic tangent):</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>tanh(z) = (e^z - e^(-z)) / (e^z + e^(-z))
</code></pre></div></div>
<ul>
  <li>Range: [-1, 1]</li>
  <li>Symmetric around 0</li>
  <li>Gradient: 1 - tanh²(z)</li>
  <li><strong>Good for:</strong> Most tasks, especially when features are centered</li>
  <li>✅ <strong>Recommended default</strong></li>
</ul>

<p><strong>sigmoid:</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sigmoid(z) = 1 / (1 + e^(-z))
</code></pre></div></div>
<ul>
  <li>Range: [0, 1]</li>
  <li>Asymmetric</li>
  <li>Gradient: sigmoid(z) · (1 - sigmoid(z))</li>
  <li><strong>Good for:</strong> When outputs are probabilities or rates</li>
</ul>

<p><strong>Why bounded activations?</strong></p>

<p>BCN requires bounded activations for theoretical guarantees and stability of the ξ criterion. Unbounded activations like ReLU are <strong>not recommended</strong> because:</p>
<ol>
  <li>Theoretical issues: The ξ optimization assumes bounded activation <em>outputs</em></li>
  <li>Stability: Unbounded outputs can destabilize the ensemble</li>
  <li>While ReLU could <em>theoretically</em> work with very tight weight constraints (small λ), tanh/sigmoid provide stronger guarantees</li>
</ol>

<p><strong>Rule of thumb:</strong> Use <strong>tanh</strong> as default. It’s more balanced, bounded and zero-centered.</p>

<hr />

<h2 id="hyperparameter-7-tol-early-stopping-tolerance">Hyperparameter 7: <code class="language-plaintext highlighter-rouge">tol</code> (Early Stopping Tolerance)</h2>

<p><strong>Default:</strong> 0 (no early stopping)<br />
<strong>Typical range:</strong> 1e-7 to 1e-3<br />
<strong>Recommended:</strong> 1e-7 for most tasks</p>

<p><strong>What it controls:</strong> When to stop training before reaching B iterations</p>

<p><strong>Intuition:</strong> If the model stops improving (residual norm isn’t decreasing much), stop early to avoid overfitting and save computation.</p>

<p><strong>How it works (corrected):</strong>
BCN tracks the relative improvement in residual norm and stops if progress is too slow:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>if (relative_decrease_in_residuals &lt; tol):
    stop training
</code></pre></div></div>

<p><strong>Important clarification:</strong> Early stopping is based on <strong>improvement rate</strong>, not absolute residual magnitude. This means BCN can stop even when residuals are still large (on a hard problem) if adding more weak learners doesn’t help.</p>

<p><strong>Trade-offs:</strong></p>

<p><strong>tol = 0 (no early stopping):</strong></p>

<ul>
  <li>Always trains for exactly B iterations</li>
  <li>May overfit if B is too large</li>
  <li>✅ Use for: Quick experiments with small B</li>
</ul>

<p><strong>tol = 1e-7 to 1e-5:</strong></p>

<ul>
  <li>Stops when improvement becomes negligible</li>
  <li>Prevents overfitting</li>
  <li>Can save significant computation</li>
  <li>✅ Use for: Production models with large B</li>
</ul>

<p><strong>Practical tip:</strong> Set B large (e.g., 500-1000) and tol small (e.g., 1e-7) to let the algorithm decide when to stop. The actual number of iterations used will be stored in <code class="language-plaintext highlighter-rouge">fit$maxL</code>.</p>

<hr />

<h2 id="hyperparameter-8-type_optim-optimization-method">Hyperparameter 8: <code class="language-plaintext highlighter-rouge">type_optim</code> (Optimization Method)</h2>

<p>Gradient-based.</p>

<p><strong>Default:</strong> “nlminb”<br />
<strong>Options:</strong> “nlminb”, “adam”, “sgd”, “nmkb”, “hjkb”, “randomsearch”</p>

<p><strong>What it controls:</strong> How to solve the optimization problem at each iteration</p>

<p><strong>Intuition:</strong> Finding the best weights w_L is a <strong>non-convex optimization</strong> problem. Different solvers have different trade-offs.</p>

<p><strong>Available optimizers:</strong></p>

<p><strong>nlminb</strong> (default):</p>

<ul>
  <li>Uses gradient and Hessian approximations</li>
  <li>✅ Robust</li>
  <li>✅ Well-tested in R</li>
  <li>✅ Works well in most cases</li>
  <li>⚠️ Medium speed</li>
  <li>✅ Use for: General purpose, production</li>
</ul>

<p><strong>adam / sgd:</strong></p>

<ul>
  <li>Gradient-based optimizers from deep learning</li>
  <li>✅ Fast, especially for high-dimensional problems</li>
  <li>✅ Good for large d (many features)</li>
  <li>⚠️ May need tuning (learning rate, iterations)</li>
  <li>✅ Use for: d &gt; 100, speed-critical applications</li>
</ul>

<p><strong>nmkb / hjkb:</strong></p>

<ul>
  <li>Derivative-free Nelder-Mead / Hooke-Jeeves</li>
  <li>✅ Very robust (no gradient needed)</li>
  <li>❌ Slow</li>
  <li>✅ Use when: Other optimizers fail or diverge</li>
</ul>

<p><strong>randomsearch:</strong></p>

<ul>
  <li>Random sampling + local search</li>
  <li>✅ Can escape local minima</li>
  <li>❌ Slower</li>
  <li>✅ Use when: Problem is very non-convex</li>
</ul>

<p><strong>Rule of thumb:</strong></p>

<ul>
  <li>Start with <code class="language-plaintext highlighter-rouge">"nlminb"</code></li>
  <li>If training is slow and d &gt; 100, try <code class="language-plaintext highlighter-rouge">"adam"</code></li>
  <li>Can pass additional arguments via <code class="language-plaintext highlighter-rouge">...</code> (e.g., max iterations, tolerance)</li>
</ul>

<p><strong>Important insight:</strong>
Because BCN uses an ensemble, <strong>local optima are OK</strong>! Even if we don’t find the globally optimal w_L, the next iteration can compensate. This is why BCN is robust despite non-convex optimization at each step.</p>

<hr />

<h2 id="hyperparameter-9-hidden_layer_bias-include-bias-term">Hyperparameter 9: <code class="language-plaintext highlighter-rouge">hidden_layer_bias</code> (Include Bias Term)</h2>

<p><strong>Default:</strong> TRUE<br />
<strong>Options:</strong> TRUE, FALSE</p>

<p><strong>What it controls:</strong> Whether neural networks have a bias/intercept term</p>

<p><strong>Intuition:</strong> Without bias, h_L = activation(w^T x). With bias, h_L = activation(w^T x + b).</p>

<p><strong>Trade-offs:</strong></p>

<p><strong>hidden_layer_bias = FALSE:</strong></p>

<ul>
  <li>Simpler optimization (one less parameter per iteration)</li>
  <li>Faster training</li>
  <li>Assumes data is centered</li>
  <li>✅ Use when: Features are already centered, want pure multiplicative effects</li>
</ul>

<p><strong>hidden_layer_bias = TRUE:</strong></p>

<ul>
  <li>More expressive (can handle shifts)</li>
  <li>Can handle non-centered data better</li>
  <li>One additional parameter to optimize per iteration</li>
  <li>✅ <strong>Recommended default</strong> - safer choice</li>
</ul>

<p><strong>Typical choice:</strong> Use TRUE unless you have a specific reason not to (e.g., theoretical interest in purely multiplicative models).</p>

<hr />

<h2 id="hyperparameter-10-n_clusters-optional-clustering-features">Hyperparameter 10: <code class="language-plaintext highlighter-rouge">n_clusters</code> (Optional Clustering Features)</h2>

<p><strong>Default:</strong> NULL (no clustering)<br />
<strong>Typical range:</strong> 2-10</p>

<p><strong>What it controls:</strong> Whether to add cluster membership features</p>

<p><strong>Intuition:</strong> BCN can automatically perform k-means clustering on your inputs and add cluster memberships as additional features. This can help capture local patterns.</p>

<p><strong>When to use:</strong></p>

<ul>
  <li>✅ Data has natural groupings or modes</li>
  <li>✅ Local patterns differ across regions of feature space</li>
  <li>❌ Not needed for most standard regression/classification</li>
</ul>

<p><strong>Note:</strong> This is an advanced feature - start without it and add only if needed.</p>

<hr />

<h2 id="putting-it-all-together-hyperparameter-recipes">Putting It All Together: Hyperparameter Recipes</h2>

<h3 id="recipe-1-fast-prototyping-small-dataset-n--1000">Recipe 1: Fast Prototyping (Small Dataset, N &lt; 1000)</h3>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">fit</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">bcn</span><span class="p">(</span><span class="w">
  </span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">X_train</span><span class="p">,</span><span class="w"> 
  </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">y_train</span><span class="p">,</span><span class="w">
  </span><span class="n">B</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">50</span><span class="p">,</span><span class="w">              </span><span class="c1"># Few iterations for speed</span><span class="w">
  </span><span class="n">nu</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.5</span><span class="p">,</span><span class="w">            </span><span class="c1"># Moderate learning rate</span><span class="w">
  </span><span class="n">col_sample</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">1.0</span><span class="p">,</span><span class="w">    </span><span class="c1"># Use all features (dataset is small)</span><span class="w">
  </span><span class="n">lam</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">10</span><span class="o">^</span><span class="m">0.5</span><span class="p">,</span><span class="w">        </span><span class="c1"># ~3.16, moderate regularization</span><span class="w">
  </span><span class="n">r</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.9</span><span class="p">,</span><span class="w">             </span><span class="c1"># Adaptive threshold</span><span class="w">
  </span><span class="n">tol</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">1e-5</span><span class="p">,</span><span class="w">          </span><span class="c1"># Early stopping</span><span class="w">
  </span><span class="n">activation</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"tanh"</span><span class="p">,</span><span class="w">
  </span><span class="n">type_optim</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"nlminb"</span><span class="p">,</span><span class="w">
  </span><span class="n">hidden_layer_bias</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="w">
</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p><strong>Why these choices:</strong></p>
<ul>
  <li>Small B for speed</li>
  <li>High nu for faster convergence</li>
  <li>No column sampling (dataset is small)</li>
  <li>Standard other parameters</li>
</ul>

<p><strong>Expected performance:</strong> Quick baseline in minutes</p>

<hr />

<h3 id="recipe-2-production-model-medium-dataset-n--10000">Recipe 2: Production Model (Medium Dataset, N ~ 10,000)</h3>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">fit</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">bcn</span><span class="p">(</span><span class="w">
  </span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">X_train</span><span class="p">,</span><span class="w">
  </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">y_train</span><span class="p">,</span><span class="w">
  </span><span class="n">B</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">200</span><span class="p">,</span><span class="w">             </span><span class="c1"># Enough iterations with early stopping</span><span class="w">
  </span><span class="n">nu</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.3</span><span class="p">,</span><span class="w">            </span><span class="c1"># Conservative for stability</span><span class="w">
  </span><span class="n">col_sample</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.6</span><span class="p">,</span><span class="w">    </span><span class="c1"># Some regularization</span><span class="w">
  </span><span class="n">lam</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">10</span><span class="o">^</span><span class="m">0.8</span><span class="p">,</span><span class="w">        </span><span class="c1"># ~6.31, allow some complexity</span><span class="w">
  </span><span class="n">r</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.95</span><span class="p">,</span><span class="w">            </span><span class="c1"># Very selective early on</span><span class="w">
  </span><span class="n">tol</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">1e-7</span><span class="p">,</span><span class="w">          </span><span class="c1"># Train until converged</span><span class="w">
  </span><span class="n">activation</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"tanh"</span><span class="p">,</span><span class="w">
  </span><span class="n">type_optim</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"nlminb"</span><span class="p">,</span><span class="w">
  </span><span class="n">hidden_layer_bias</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="w">
</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p><strong>Why these choices:</strong></p>
<ul>
  <li>Moderate B with early stopping safety</li>
  <li>Conservative nu for stability</li>
  <li>Column sampling for regularization</li>
  <li>High r for careful feature selection</li>
</ul>

<p><strong>Expected performance:</strong> Robust model, may train 100-150 iterations before stopping</p>

<hr />

<h3 id="recipe-3-complex-task-large-dataset-high-dimensional">Recipe 3: Complex Task (Large Dataset, High-Dimensional)</h3>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">fit</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">bcn</span><span class="p">(</span><span class="w">
  </span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">X_train</span><span class="p">,</span><span class="w">
  </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">y_train</span><span class="p">,</span><span class="w">
  </span><span class="n">B</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">500</span><span class="p">,</span><span class="w">             </span><span class="c1"># Many iterations (will stop early if needed)</span><span class="w">
  </span><span class="n">nu</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.4</span><span class="p">,</span><span class="w">            </span><span class="c1"># Balanced</span><span class="w">
  </span><span class="n">col_sample</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.5</span><span class="p">,</span><span class="w">    </span><span class="c1"># Strong regularization for high d</span><span class="w">
  </span><span class="n">lam</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">10</span><span class="o">^</span><span class="m">1.0</span><span class="p">,</span><span class="w">        </span><span class="c1"># 10, higher complexity allowed</span><span class="w">
  </span><span class="n">r</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.95</span><span class="p">,</span><span class="w">            </span><span class="c1"># Adaptive</span><span class="w">
  </span><span class="n">tol</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">1e-7</span><span class="p">,</span><span class="w">          </span><span class="c1"># Early stopping safety</span><span class="w">
  </span><span class="n">activation</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"tanh"</span><span class="p">,</span><span class="w">
  </span><span class="n">type_optim</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"adam"</span><span class="p">,</span><span class="w">  </span><span class="c1"># Fast optimizer for high d</span><span class="w">
  </span><span class="n">hidden_layer_bias</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="w">
</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p><strong>Why these choices:</strong></p>
<ul>
  <li>Large B to capture complexity</li>
  <li>Column sampling crucial for high dimensions (d &gt; 100)</li>
  <li>Adam optimizer for speed with many features</li>
  <li>High r to prevent early overfitting</li>
</ul>

<p><strong>Expected performance:</strong> May use 200-400 iterations, handles d &gt; 500 well</p>

<hr />

<h3 id="recipe-4-multivariate-time-series--multi-output">Recipe 4: Multivariate Time Series / Multi-Output</h3>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">fit</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">bcn</span><span class="p">(</span><span class="w">
  </span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">X_train</span><span class="p">,</span><span class="w">
  </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Y_train</span><span class="p">,</span><span class="w">         </span><span class="c1"># Matrix with multiple outputs (e.g., N x m)</span><span class="w">
  </span><span class="n">B</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">300</span><span class="p">,</span><span class="w">
  </span><span class="n">nu</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.5</span><span class="p">,</span><span class="w">            </span><span class="c1"># Can be higher for shared structure</span><span class="w">
  </span><span class="n">col_sample</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.7</span><span class="p">,</span><span class="w">
  </span><span class="n">lam</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">10</span><span class="o">^</span><span class="m">0.7</span><span class="p">,</span><span class="w">
  </span><span class="n">r</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.95</span><span class="p">,</span><span class="w">            </span><span class="c1"># Critical: enforces shared structure</span><span class="w">
  </span><span class="n">tol</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">1e-7</span><span class="p">,</span><span class="w">
  </span><span class="n">activation</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"tanh"</span><span class="p">,</span><span class="w">
  </span><span class="n">type_optim</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"nlminb"</span><span class="w">
</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p><strong>Why these choices:</strong></p>
<ul>
  <li><strong>High r is critical</strong>: In multivariate mode, BCN computes ξ_k for each output k and requires min_k(ξ_k) ≥ 0 for acceptance. This ensures each weak learner contributes meaningfully across <strong>all</strong> time series/outputs, creating shared representations.</li>
  <li>Higher nu because shared structure is more stable</li>
  <li>Standard B with early stopping</li>
</ul>

<p><strong>Note on multivariate:</strong> BCN handles multiple outputs naturally through one-hot encoding (classification) or matrix targets (regression). The min(ξ) criterion prevents sacrificing one output to improve another.</p>

<p><strong>Expected performance:</strong> Strong on related time series or multi-task learning</p>

<hr />

<h2 id="understanding-hyperparameter-interactions">Understanding Hyperparameter Interactions</h2>

<h3 id="interaction-1-nu--b--constant">Interaction 1: <code class="language-plaintext highlighter-rouge">nu</code> × <code class="language-plaintext highlighter-rouge">B</code> ≈ Constant</h3>

<p><strong>Trade-off:</strong> Small nu needs large B</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Approximately equivalent final predictions:</span><span class="w">
</span><span class="n">fit1</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">bcn</span><span class="p">(</span><span class="n">B</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">100</span><span class="p">,</span><span class="w"> </span><span class="n">nu</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.5</span><span class="p">)</span><span class="w">
</span><span class="n">fit2</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">bcn</span><span class="p">(</span><span class="n">B</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">200</span><span class="p">,</span><span class="w"> </span><span class="n">nu</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.25</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p><strong>Why:</strong> Smaller steps need more iterations to reach similar places.</p>

<p><strong>Rule:</strong> For similar model complexity, <code class="language-plaintext highlighter-rouge">nu × B ≈ constant</code> (approximately).</p>

<p><strong>In practice:</strong></p>
<ul>
  <li>Production (stability priority): nu = 0.3, B = 300</li>
  <li>Prototyping (speed priority): nu = 0.5, B = 100</li>
</ul>

<hr />

<h3 id="interaction-2-lam--r-complexity-control">Interaction 2: <code class="language-plaintext highlighter-rouge">lam</code> ↔ <code class="language-plaintext highlighter-rouge">r</code> (Complexity Control)</h3>

<p><strong>Both control complexity:</strong></p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">lam</code>: How complex each weak learner can be</li>
  <li><code class="language-plaintext highlighter-rouge">r</code>: How selective we are about accepting weak learners</li>
</ul>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># More regularization</span><span class="w">
</span><span class="n">fit_reg</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">bcn</span><span class="p">(</span><span class="n">lam</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">1.0</span><span class="p">,</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.95</span><span class="p">)</span><span class="w">   </span><span class="c1"># Simple features, selective</span><span class="w">

</span><span class="c1"># Less regularization  </span><span class="w">
</span><span class="n">fit_complex</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">bcn</span><span class="p">(</span><span class="n">lam</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">10.0</span><span class="p">,</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.7</span><span class="p">)</span><span class="w">  </span><span class="c1"># Complex features, permissive</span><span class="w">
</span></code></pre></div></div>

<p><strong>Balance principle:</strong> If you allow complex features (high lam), be selective (high r) to avoid noise.</p>

<p><strong>Typical combinations:</strong></p>
<ul>
  <li><strong>High quality</strong>: lam = 10, r = 0.95 → Complex but carefully selected features</li>
  <li><strong>Moderate</strong>: lam = 5, r = 0.90 → Balanced</li>
  <li><strong>Fast/loose</strong>: lam = 3, r = 0.80 → Simple features, permissive</li>
</ul>

<hr />

<h3 id="interaction-3-col_sample--b-coverage">Interaction 3: <code class="language-plaintext highlighter-rouge">col_sample</code> ↔ <code class="language-plaintext highlighter-rouge">B</code> (Coverage)</h3>

<p><strong>Column sampling as implicit regularization:</strong></p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Fewer features per iteration → need more iterations for coverage</span><span class="w">
</span><span class="n">fit1</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">bcn</span><span class="p">(</span><span class="n">col_sample</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">1.0</span><span class="p">,</span><span class="w"> </span><span class="n">B</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">100</span><span class="p">)</span><span class="w">
</span><span class="n">fit2</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">bcn</span><span class="p">(</span><span class="n">col_sample</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.5</span><span class="p">,</span><span class="w"> </span><span class="n">B</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">200</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p><strong>Rough guideline:</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>B_needed ≈ B_baseline / col_sample
</code></pre></div></div>

<p><strong>In practice:</strong></p>
<ul>
  <li>col_sample = 1.0 → B = 100-200</li>
  <li>col_sample = 0.5 → B = 200-400</li>
  <li>col_sample = 0.3 → B = 300-500</li>
</ul>

<hr />

<h2 id="the-mathematical-connection-how-hyperparameters-appear-in-ξ">The Mathematical Connection: How Hyperparameters Appear in ξ</h2>

<p>The core optimization criterion ties everything together:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ξ_L = ν(2-ν) · β²_L - [1 - r - (1-r)/(L+1)] · ||residuals||²
      └─┬──┘   └┬─┘   └──────────┬──────────┘
       nu    optimized over        r
             w ∈ [-lam, lam]
</code></pre></div></div>

<p><strong>Reading the formula:</strong></p>
<ol>
  <li>Find w_L (constrained by <code class="language-plaintext highlighter-rouge">lam</code>) that maximizes β²_L
    <ul>
      <li>
        <table>
          <tbody>
            <tr>
              <td>β_L is the OLS coefficient: β_L = (h_L^T · residuals) /</td>
              <td> </td>
              <td>h_L</td>
              <td> </td>
              <td>²</td>
            </tr>
          </tbody>
        </table>
      </li>
    </ul>
  </li>
  <li>Scale by ν(2-ν) (controlled by <code class="language-plaintext highlighter-rouge">nu</code>)</li>
  <li>Subtract penalty (controlled by <code class="language-plaintext highlighter-rouge">r</code>)</li>
  <li>Accept only if ξ ≥ 0 for all outputs</li>
  <li>Repeat for <code class="language-plaintext highlighter-rouge">B</code> iterations (or until <code class="language-plaintext highlighter-rouge">tol</code> reached)</li>
  <li>At each step, sample <code class="language-plaintext highlighter-rouge">col_sample</code> fraction of features</li>
</ol>

<p>This unified view shows how all hyperparameters work together to control the greedy feature selection process.</p>

<hr />

<h2 id="practical-tips-for-hyperparameter-tuning">Practical Tips for Hyperparameter Tuning</h2>

<h3 id="start-simple-add-complexity">Start Simple, Add Complexity</h3>

<ol>
  <li><strong>Begin with defaults:</strong>
    <div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">fit</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">bcn</span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="p">,</span><span class="w"> </span><span class="n">B</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">100</span><span class="p">,</span><span class="w"> </span><span class="n">nu</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.3</span><span class="p">,</span><span class="w"> </span><span class="n">lam</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">10</span><span class="o">^</span><span class="m">0.7</span><span class="p">,</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.9</span><span class="p">)</span><span class="w">
</span></code></pre></div>    </div>
  </li>
  <li><strong>If underfitting (train error too high):</strong>
    <ul>
      <li>↑ Increase B (more capacity)</li>
      <li>↑ Increase lam (allow more complex features)</li>
      <li>↑ Increase nu (use features more aggressively)</li>
      <li>↓ Decrease r (be less selective)</li>
    </ul>
  </li>
  <li><strong>If overfitting (train « test error):</strong>
    <ul>
      <li>↓ Decrease nu (smaller, more careful steps)</li>
      <li>↓ Decrease lam (simpler features)</li>
      <li>Add column sampling (col_sample = 0.5-0.7)</li>
      <li>↑ Increase r (be more selective)</li>
      <li>Use early stopping (tol = 1e-7)</li>
    </ul>
  </li>
</ol>

<h3 id="use-cross-validation-wisely">Use Cross-Validation Wisely</h3>

<p><strong>Most important to tune:</strong> <code class="language-plaintext highlighter-rouge">B</code>, <code class="language-plaintext highlighter-rouge">nu</code>, <code class="language-plaintext highlighter-rouge">lam</code></p>

<p><strong>Moderately important:</strong> <code class="language-plaintext highlighter-rouge">r</code>, <code class="language-plaintext highlighter-rouge">col_sample</code></p>

<p><strong>Usually fixed:</strong> <code class="language-plaintext highlighter-rouge">hidden_layer_bias = TRUE</code>, <code class="language-plaintext highlighter-rouge">type_optim = "nlminb"</code>, <code class="language-plaintext highlighter-rouge">activation = "tanh"</code></p>

<p><strong>Example CV strategy:</strong></p>
<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">library</span><span class="p">(</span><span class="n">caret</span><span class="p">)</span><span class="w">

</span><span class="c1"># Grid search on log-scale for lam</span><span class="w">
</span><span class="n">grid</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">expand.grid</span><span class="p">(</span><span class="w">
  </span><span class="n">B</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="m">100</span><span class="p">,</span><span class="w"> </span><span class="m">200</span><span class="p">,</span><span class="w"> </span><span class="m">500</span><span class="p">),</span><span class="w">
  </span><span class="n">nu</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="m">0.1</span><span class="p">,</span><span class="w"> </span><span class="m">0.3</span><span class="p">,</span><span class="w"> </span><span class="m">0.5</span><span class="p">),</span><span class="w">
  </span><span class="n">lam</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">10</span><span class="o">^</span><span class="n">seq</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="w"> </span><span class="m">1.5</span><span class="p">,</span><span class="w"> </span><span class="n">by</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.5</span><span class="p">)</span><span class="w">  </span><span class="c1"># 1, 3.16, 10, 31.6</span><span class="w">
</span><span class="p">)</span><span class="w">

</span><span class="c1"># Use caret, mlr3, or tidymodels for CV</span><span class="w">
</span></code></pre></div></div>

<h3 id="monitor-training">Monitor Training</h3>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Enable verbose output</span><span class="w">
</span><span class="n">fit</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">bcn</span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="p">,</span><span class="w"> </span><span class="n">verbose</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">1</span><span class="p">,</span><span class="w"> </span><span class="n">show_progress</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p><strong>Watch for:</strong></p>
<ul>
  <li>
    <table>
      <tbody>
        <tr>
          <td>How fast</td>
          <td> </td>
          <td>residuals</td>
          <td> </td>
          <td>_F decreases (convergence rate)</td>
        </tr>
      </tbody>
    </table>
  </li>
  <li>Whether ξ stays positive (quality of weak learners)</li>
  <li>If training stops early and at what iteration (capacity needs)</li>
</ul>

<p><strong>Diagnostic patterns:</strong></p>
<ul>
  <li>Residuals plateau early → Increase B or lam</li>
  <li>ξ often negative → Decrease r or increase lam</li>
  <li>Training very slow → Try adam optimizer or increase col_sample</li>
</ul>

<hr />

<h2 id="quick-reference-hyperparameter-cheat-sheet">Quick Reference: Hyperparameter Cheat Sheet</h2>

<table>
  <thead>
    <tr>
      <th>Hyperparameter</th>
      <th>Low Value Effect</th>
      <th>High Value Effect</th>
      <th>Typical Range</th>
      <th>Default</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>B</strong></td>
      <td>Simple, fast, may underfit</td>
      <td>Complex, slow, may overfit</td>
      <td>50-1000</td>
      <td>100</td>
    </tr>
    <tr>
      <td><strong>nu</strong></td>
      <td>Stable, slow convergence</td>
      <td>Fast, potentially unstable</td>
      <td>0.1-0.8</td>
      <td>0.1</td>
    </tr>
    <tr>
      <td><strong>lam</strong></td>
      <td>Linear-ish, simple features</td>
      <td>Nonlinear, complex features</td>
      <td>1-100</td>
      <td>0.1</td>
    </tr>
    <tr>
      <td><strong>r</strong></td>
      <td>Permissive, accepts more</td>
      <td>Selective, high quality</td>
      <td>0.3-0.99</td>
      <td>0.3</td>
    </tr>
    <tr>
      <td><strong>col_sample</strong></td>
      <td>No regularization</td>
      <td>Strong regularization</td>
      <td>0.3-1.0</td>
      <td>1.0</td>
    </tr>
    <tr>
      <td><strong>tol</strong></td>
      <td>No early stop</td>
      <td>Aggressive early stop</td>
      <td>0-1e-3</td>
      <td>0</td>
    </tr>
    <tr>
      <td><strong>activation</strong></td>
      <td>tanh (symmetric)</td>
      <td>sigmoid (asymmetric)</td>
      <td>-</td>
      <td>tanh</td>
    </tr>
    <tr>
      <td><strong>type_optim</strong></td>
      <td>nlminb (robust)</td>
      <td>adam (fast)</td>
      <td>-</td>
      <td>nlminb</td>
    </tr>
    <tr>
      <td><strong>hidden_layer_bias</strong></td>
      <td>Simpler, through origin</td>
      <td>More flexible</td>
      <td>-</td>
      <td>TRUE</td>
    </tr>
  </tbody>
</table>

<hr />

<h2 id="when-not-to-use-bcn">When NOT to Use BCN</h2>

<p>While BCN is versatile, it’s not always the best choice:</p>

<p>❌ <strong>Ultra-high-dimensional sparse data (d &gt; 10,000)</strong></p>
<ul>
  <li>Tree-based boosting (XGBoost/LightGBM) may be faster</li>
  <li>Column sampling helps, but trees handle sparsity natively</li>
</ul>

<p>❌ <strong>Very large datasets (N &gt; 1,000,000)</strong></p>
<ul>
  <li>Training time scales roughly O(B × N × d)</li>
  <li>Consider subsampling or streaming methods</li>
</ul>

<p>❌ <strong>Deep sequential/temporal structure</strong></p>
<ul>
  <li>BCN is static (no recurrence)</li>
  <li>Use RNNs/Transformers for complex time dependencies</li>
</ul>

<p>❌ <strong>Image/text/audio from scratch</strong></p>
<ul>
  <li>Convolutional/attention architectures more suitable</li>
  <li>BCN works on extracted features (embeddings, tabular)</li>
</ul>

<p>✅ <strong>BCN shines at:</strong></p>
<ul>
  <li>Tabular data (100s to 10,000s of rows)</li>
  <li>Multivariate prediction (shared structure across outputs)</li>
  <li>Needing both accuracy AND interpretability</li>
  <li>Time series with extracted features</li>
  <li>When XGBoost works but you want gradient-based explanations</li>
</ul>

<hr />

<h2 id="debugging-bcn-training">Debugging BCN Training</h2>

<table>
  <thead>
    <tr>
      <th>Symptom</th>
      <th>Likely Cause</th>
      <th>Fix</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>ξ frequently negative early</td>
      <td>r too high or lam too low</td>
      <td>Decrease r to 0.8 or increase lam to 5-10</td>
    </tr>
    <tr>
      <td>Residuals plateau quickly</td>
      <td>nu too small or B too low</td>
      <td>Increase nu to 0.4-0.5 or B to 300+</td>
    </tr>
    <tr>
      <td>Training very slow</td>
      <td>col_sample=1 on wide data</td>
      <td>Set col_sample=0.5 and try type_optim=”adam”</td>
    </tr>
    <tr>
      <td>High train accuracy, poor test</td>
      <td>Overfitting</td>
      <td>Decrease nu, increase r, add col_sample &lt; 1</td>
    </tr>
    <tr>
      <td>Poor train accuracy</td>
      <td>Underfitting</td>
      <td>Increase B, increase lam, try different activation</td>
    </tr>
    <tr>
      <td>Optimizer not converging</td>
      <td>Bad initialization or scaling</td>
      <td>Check feature scaling, try different type_optim</td>
    </tr>
  </tbody>
</table>

<hr />

<h2 id="interpretability-example">Interpretability Example</h2>

<p>One of BCN’s unique advantages is <strong>gradient-based interpretability</strong>.</p>

<p><strong>What makes this special:</strong></p>
<ul>
  <li>✅ Exact analytic gradients (no approximation)</li>
  <li>✅ Same O(B × m × d) cost as prediction</li>
  <li>✅ Shows direction of influence (positive/negative)</li>
  <li>✅ Works for both regression and classification</li>
  <li>✅ Much faster than SHAP on tree ensembles</li>
</ul>

<hr />

<h2 id="conclusion-the-philosophy-of-bcn">Conclusion: The Philosophy of BCN</h2>

<p>BCN’s hyperparameters reveal its design philosophy:</p>

<p><strong>1. Iterative Refinement</strong> (via <code class="language-plaintext highlighter-rouge">B</code>)<br />
Build the model piece by piece, adding one well-chosen feature at a time.</p>

<p><strong>2. Conservative Steps</strong> (via <code class="language-plaintext highlighter-rouge">nu</code>)<br />
Don’t trust any single feature too much - combine many weak learners.</p>

<p><strong>3. Bounded Complexity</strong> (via <code class="language-plaintext highlighter-rouge">lam</code>)<br />
Keep individual weak learners simple to ensure stability and interpretability.</p>

<p><strong>4. Adaptive Selection</strong> (via <code class="language-plaintext highlighter-rouge">r</code>)<br />
Start picky (prevent early mistakes), become permissive (allow refinement).</p>

<p><strong>5. Randomization</strong> (via <code class="language-plaintext highlighter-rouge">col_sample</code>)<br />
Like Random Forests, diversity through randomness helps generalization.</p>

<p><strong>6. Early Stopping</strong> (via <code class="language-plaintext highlighter-rouge">tol</code>)<br />
Know when to stop - more iterations aren’t always better.</p>

<p><strong>7. Explicit Optimization for Interpretability</strong><br />
Unlike methods that require post-hoc explanations, BCN is designed with interpretability in mind through its additive structure and differentiable components.</p>

<p>Together, these create a model that’s:</p>
<ul>
  <li>✅ <strong>Expressive</strong> (neural network features capture non-linearity)</li>
  <li>✅ <strong>Interpretable</strong> (additive structure + gradients)</li>
  <li>✅ <strong>Robust</strong> (ensemble of bounded weak learners)</li>
  <li>✅ <strong>Efficient</strong> (sparse structure, early stopping, column sampling)</li>
</ul>

<hr />

<h2 id="next-steps">Next Steps</h2>

<p><strong>To learn more:</strong></p>
<ul>
  <li>📦 <a href="https://github.com/Techtonique/bcn">BCN R Package on GitHub</a></li>
  <li>📦 <a href="https://github.com/Techtonique/bcn_python">BCN Python Package on GitHub</a></li>
  <li>📝 <a href="https://www.researchgate.net/publication/332291211_Forecasting_multivariate_time_series_with_boosted_configuration_networks">Research preprint on BCN</a></li>
</ul>

<p><strong>To contribute:</strong>
BCN is open source! Contributions welcome for:</p>
<ul>
  <li>New activation functions</li>
  <li>Additional optimization methods</li>
  <li>Interpretability visualizations</li>
  <li>Benchmark studies and applications</li>
</ul>]]></content><author><name></name></author><category term="R" /><category term="Python" /><summary type="html"><![CDATA[How BCN combine neural networks and boosting, explained through the knobs you can turn]]></summary></entry></feed>