<?xml version="1.0" encoding="UTF-8"?>
<rss  xmlns:atom="http://www.w3.org/2005/Atom" 
      xmlns:media="http://search.yahoo.com/mrss/" 
      xmlns:content="http://purl.org/rss/1.0/modules/content/" 
      xmlns:dc="http://purl.org/dc/elements/1.1/" 
      version="2.0">
<channel>
<title>Kwiz Computing Technologies</title>
<link>https://kwizresearch.com/blog/</link>
<atom:link href="https://kwizresearch.com/blog/index.xml" rel="self" type="application/rss+xml"/>
<description>Insights on environmental data science, R programming, quantitative finance, and production-grade data systems from Kwiz Computing Technologies.</description>
<generator>quarto-1.4.554</generator>
<lastBuildDate>Thu, 23 Apr 2026 00:00:00 GMT</lastBuildDate>
<item>
  <title>Scope 3 Emissions Accounting for Kenyan Companies</title>
  <dc:creator>Kwiz Computing Technologies</dc:creator>
  <link>https://kwizresearch.com/blog/posts/esg-scope3-emissions-accounting-r-kenya-companies/</link>
  <description><![CDATA[ 




<section id="the-part-of-your-carbon-footprint-you-are-probably-ignoring" class="level2">
<h2 class="anchored" data-anchor-id="the-part-of-your-carbon-footprint-you-are-probably-ignoring">The Part of Your Carbon Footprint You Are Probably Ignoring</h2>
<p>For most Kenyan companies, Scope 3 emissions make up more than 70% of their total greenhouse gas footprint, yet virtually none of their climate disclosures touch it. That gap is not a secret: it is a measurement problem, and one that R can help close.</p>
<p>Scope 1 covers direct combustion (your generator, your fleet). Scope 2 covers purchased electricity. Scope 3 covers everything else: the emissions embedded in the goods you buy, the business travel your staff take, the waste your offices produce, and the use of products you sell. The GHG Protocol Corporate Value Chain (Scope 3) Standard, published in 2011, divides this into 15 categories across upstream and downstream activities.</p>
</section>
<section id="what-the-ghgp-scope-3-standard-actually-requires" class="level2">
<h2 class="anchored" data-anchor-id="what-the-ghgp-scope-3-standard-actually-requires">What the GHGP Scope 3 Standard Actually Requires</h2>
<p>The GHG Protocol Corporate Value Chain Standard is not a regulation. It is a widely adopted methodology framework that organises the 15 Scope 3 categories into two groups.</p>
<p><strong>Upstream categories (1-8)</strong> cover activities related to purchased inputs:</p>
<ol type="1">
<li>Purchased goods and services</li>
<li>Capital goods</li>
<li>Fuel- and energy-related activities</li>
<li>Upstream transportation and distribution</li>
<li>Waste generated in operations</li>
<li>Business travel</li>
<li>Employee commuting</li>
<li>Upstream leased assets</li>
</ol>
<p><strong>Downstream categories (9-15)</strong> cover what happens after you sell:</p>
<ol start="9" type="1">
<li>Downstream transportation and distribution</li>
<li>Processing of sold products</li>
<li>Use of sold products</li>
<li>End-of-life treatment of sold products</li>
<li>Downstream leased assets</li>
<li>Franchises</li>
<li>Investments</li>
</ol>
<p>For most Kenyan manufacturers, importers, and service firms, categories 1 (purchased goods and services) and 6 (business travel) tend to dominate. A Nairobi-based FMCG company sourcing raw materials from across East Africa will find that category 1 alone can account for 60-80% of total Scope 3.</p>
<p>The standard distinguishes between required and optional categories depending on your industry, but the expectation from voluntary frameworks like CDP and the emerging ISSB standards is that material categories must be disclosed with a credible methodology.</p>
</section>
<section id="two-methods-spend-based-and-activity-based" class="level2">
<h2 class="anchored" data-anchor-id="two-methods-spend-based-and-activity-based">Two Methods: Spend-Based and Activity-Based</h2>
<p>There are two practical approaches for quantifying Scope 3 emissions, each with different data requirements and accuracy levels.</p>
<p><strong>Activity-based methods</strong> multiply physical activity data (tonnes of material purchased, kilometres travelled, cubic metres of waste) by a corresponding emission factor. They are more accurate but require granular procurement data that most Kenyan companies do not yet collect systematically.</p>
<p><strong>Spend-based methods</strong> multiply financial spend data (cost in USD or KES) by an economic emission intensity factor, expressed in kg CO2e per unit of spend in a given industry sector. This is the practical starting point for organisations beginning their Scope 3 journey.</p>
<p>The US EPA’s Environmentally Extended Input-Output (USEEIO) model provides spend-based emission factors for 411 industry sectors of the US economy. While built on US data, these factors are used globally as a first-approximation tool. Exiobase, a multi-regional input-output database covering 44 countries and 5 rest-of-world regions, offers geographically closer factors for some East African supply chains, though it requires more pre-processing.</p>
<p>For a Kenyan company in 2026, a reasonable starting protocol is: use USEEIO factors for categories where supply chains are internationally sourced (electronics, vehicles, chemicals), and supplement with locally derived activity-based factors where Kenyan context is available.</p>
</section>
<section id="building-the-r-workflow" class="level2">
<h2 class="anchored" data-anchor-id="building-the-r-workflow">Building the R Workflow</h2>
<p>The workflow below demonstrates Scope 3 category 1 (purchased goods and services) using spend data and USEEIO emission factors. The same pattern extends to any spend-based category.</p>
<section id="step-1-load-useeio-emission-factors" class="level3">
<h3 class="anchored" data-anchor-id="step-1-load-useeio-emission-factors">Step 1: Load USEEIO Emission Factors</h3>
<p>The EPA provides the USEEIO model as a public dataset. We use the v2.0 supply chain emission factors, which are available as a CSV.</p>
<div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb1-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(tidyverse)</span>
<span id="cb1-2"></span>
<span id="cb1-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Download USEEIO v2.0 supply chain GHG factors</span></span>
<span id="cb1-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Source: https://www.epa.gov/system/files/documents/2024-01/useeio_2.0_supply_chain_ghg_emission_factors_v1.1.csv</span></span>
<span id="cb1-5">useeio_url <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">paste0</span>(</span>
<span id="cb1-6">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"https://www.epa.gov/system/files/documents/2024-01/"</span>,</span>
<span id="cb1-7">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"useeio_2.0_supply_chain_ghg_emission_factors_v1.1.csv"</span></span>
<span id="cb1-8">)</span>
<span id="cb1-9"></span>
<span id="cb1-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Read and clean</span></span>
<span id="cb1-11">useeio_factors <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">read_csv</span>(useeio_url, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">show_col_types =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">FALSE</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb1-12">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">select</span>(</span>
<span id="cb1-13">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">sector_code   =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">2017 NAICS Code</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span>,</span>
<span id="cb1-14">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">sector_name   =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">2017 NAICS Title</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span>,</span>
<span id="cb1-15">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">ghg_factor    =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">Supply Chain Emission Factors without Margins (kg CO2e/2021 USD)</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span></span>
<span id="cb1-16">  ) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb1-17">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mutate</span>(</span>
<span id="cb1-18">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">sector_code =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.character</span>(sector_code),</span>
<span id="cb1-19">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">ghg_factor  =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.numeric</span>(ghg_factor)</span>
<span id="cb1-20">  ) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb1-21">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">filter</span>(<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">is.na</span>(ghg_factor))</span>
<span id="cb1-22"></span>
<span id="cb1-23"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">head</span>(useeio_factors, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>)</span></code></pre></div>
</section>
<section id="step-2-load-your-procurement-data" class="level3">
<h3 class="anchored" data-anchor-id="step-2-load-your-procurement-data">Step 2: Load Your Procurement Data</h3>
<p>Your procurement data needs at minimum: a spend amount, a currency, and a sector or product classification. NAICS codes work directly with USEEIO; for Kenyan companies using local classifications, you will need a mapping table.</p>
<div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb2-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Example procurement data for a Kenyan manufacturer</span></span>
<span id="cb2-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># In practice, this comes from your ERP or accounts payable system</span></span>
<span id="cb2-3">procurement <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tibble</span>(</span>
<span id="cb2-4">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">category      =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Steel &amp; fabricated metals"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Plastic packaging"</span>,</span>
<span id="cb2-5">                    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Chemical inputs"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Road freight"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"IT equipment"</span>),</span>
<span id="cb2-6">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">naics_code    =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"332"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"3261"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"325"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"4841"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"334"</span>),</span>
<span id="cb2-7">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">spend_ksh     =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">12</span>_500_000, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>_800_000, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">7</span>_200_000, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>_100_000, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>_900_000),</span>
<span id="cb2-8">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">spend_usd     =</span> spend_ksh <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">130</span>  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># approximate KES/USD exchange rate</span></span>
<span id="cb2-9">)</span></code></pre></div>
</section>
<section id="step-3-join-factors-and-calculate-emissions" class="level3">
<h3 class="anchored" data-anchor-id="step-3-join-factors-and-calculate-emissions">Step 3: Join Factors and Calculate Emissions</h3>
<div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb3-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Aggregate USEEIO factors to 3-digit NAICS for the join</span></span>
<span id="cb3-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># (USEEIO provides 6-digit detail; our procurement may only have 3-digit codes)</span></span>
<span id="cb3-3">useeio_3digit <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> useeio_factors <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb3-4">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mutate</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">naics_3 =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">str_sub</span>(sector_code, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>)) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb3-5">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">summarise</span>(</span>
<span id="cb3-6">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">ghg_factor =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mean</span>(ghg_factor, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">na.rm =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>),</span>
<span id="cb3-7">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">.by =</span> naics_3</span>
<span id="cb3-8">  )</span>
<span id="cb3-9"></span>
<span id="cb3-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Join and compute</span></span>
<span id="cb3-11">scope3_cat1 <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> procurement <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb3-12">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">left_join</span>(useeio_3digit, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">by =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"naics_code"</span> <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"naics_3"</span>)) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb3-13">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mutate</span>(</span>
<span id="cb3-14">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">emissions_kg_co2e   =</span> spend_usd <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> ghg_factor,</span>
<span id="cb3-15">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">emissions_tco2e     =</span> emissions_kg_co2e <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1000</span></span>
<span id="cb3-16">  )</span>
<span id="cb3-17"></span>
<span id="cb3-18"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Summary</span></span>
<span id="cb3-19">scope3_cat1 <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb3-20">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">summarise</span>(</span>
<span id="cb3-21">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">total_spend_usd    =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sum</span>(spend_usd),</span>
<span id="cb3-22">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">total_tco2e        =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sum</span>(emissions_tco2e, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">na.rm =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>)</span>
<span id="cb3-23">  )</span></code></pre></div>
</section>
<section id="step-4-visualise-and-report" class="level3">
<h3 class="anchored" data-anchor-id="step-4-visualise-and-report">Step 4: Visualise and Report</h3>
<div class="sourceCode" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb4-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(ggplot2)</span>
<span id="cb4-2"></span>
<span id="cb4-3">scope3_cat1 <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb4-4">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">arrange</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">desc</span>(emissions_tco2e)) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb4-5">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mutate</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">category =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">fct_inorder</span>(category)) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb4-6">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ggplot</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">aes</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">x =</span> emissions_tco2e, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">y =</span> category, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">fill =</span> emissions_tco2e)) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb4-7">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">geom_col</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">show.legend =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">FALSE</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb4-8">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">scale_fill_gradient</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">low =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#aed6b1"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">high =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#2d6a4f"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb4-9">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">scale_x_continuous</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">labels =</span> scales<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span>comma) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb4-10">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">labs</span>(</span>
<span id="cb4-11">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">title    =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Scope 3 Category 1: Purchased Goods and Services"</span>,</span>
<span id="cb4-12">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">subtitle =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Spend-based estimates using USEEIO v2.0 emission factors"</span>,</span>
<span id="cb4-13">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">x        =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Estimated emissions (tCO2e)"</span>,</span>
<span id="cb4-14">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">y        =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">NULL</span>,</span>
<span id="cb4-15">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">caption  =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Emission factors: EPA USEEIO v2.0 | Spend converted at 130 KES/USD"</span></span>
<span id="cb4-16">  ) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb4-17">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">theme_minimal</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">base_size =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">13</span>)</span></code></pre></div>
<p>This gives you a hotspot chart showing which procurement categories carry the most embedded carbon. The steel and chemicals categories almost always dominate for manufacturers, pointing to where supplier engagement will have the most impact.</p>
</section>
</section>
<section id="kenya-specific-context-nse-nema-and-what-is-coming" class="level2">
<h2 class="anchored" data-anchor-id="kenya-specific-context-nse-nema-and-what-is-coming">Kenya-Specific Context: NSE, NEMA, and What Is Coming</h2>
<p>No Kenyan statute currently mandates Scope 3 disclosure. But three forces are changing the calculation for NSE-listed companies and large private firms.</p>
<p>First, the Capital Markets Authority’s draft sustainability reporting guidelines, aligned with ISSB’s IFRS S2, include climate-related disclosures. IFRS S2 explicitly references the GHG Protocol and expects material Scope 3 categories to be disclosed once the standard is adopted, which is anticipated for listed companies.</p>
<p>Second, multinational buyers are increasingly passing climate requirements down their supply chains. A Kenyan exporter selling to European retailers operating under the EU Corporate Sustainability Reporting Directive (CSRD) will find that their Scope 3 category 1 emissions become their customer’s Scope 3 category 1 concern. Supply chain pressure is already the fastest path to mandatory Scope 3 disclosure for Kenyan exporters.</p>
<p>Third, NEMA’s environmental management framework continues to expand. While NEMA’s current focus is on direct environmental impacts assessed through EIAs (see our post on <a href="../../../blog/posts/esia-reproducibility/">ESIA reproducibility</a>), GHG accounting is increasingly part of environmental management plans for large projects.</p>
<p>The East African context also shapes which Scope 3 categories matter most. Companies sourcing from smallholder agricultural supply chains in the region (coffee, tea, cut flowers, macadamia) carry significant upstream land-use change emissions in category 1 that USEEIO factors, built on US industry data, will systematically underestimate. For these sectors, activity-based accounting with Africa-specific emission factors from sources like the IPCC national inventory guidelines or the Cool Farm Tool is more appropriate.</p>
</section>
<section id="limitations-and-the-path-to-better-data" class="level2">
<h2 class="anchored" data-anchor-id="limitations-and-the-path-to-better-data">Limitations and the Path to Better Data</h2>
<p>The spend-based method is a starting point, not a final answer. USEEIO factors represent average US industrial emissions intensity, which diverges from Kenyan supply chain reality in significant ways. The electricity grid in Kenya is far cleaner than the US average (roughly 90% renewable generation), so any factor applied to energy-intensive local processes will overestimate actual emissions. Conversely, for imported goods produced in coal-heavy economies, USEEIO may underestimate.</p>
<p>The improvement path is sequential. Start with spend-based estimates to identify material hotspots. Then focus data collection effort on the top two or three categories by estimated emissions. Transition those categories to activity-based accounting using actual supplier data or industry-specific factors. The remaining categories can stay on spend-based estimates indefinitely if they are not material.</p>
<p>For organisations building this capability from scratch, our <a href="../../../blog/posts/kenya-open-data/">Kenya open data post</a> covers how to access public datasets programmatically. The same principles apply to structuring your internal ESG data for repeatable analysis.</p>
</section>
<section id="building-a-defensible-auditable-ghg-inventory" class="level2">
<h2 class="anchored" data-anchor-id="building-a-defensible-auditable-ghg-inventory">Building a Defensible, Auditable GHG Inventory</h2>
<p>A Scope 3 inventory only has value if it can withstand scrutiny. Three practices make the difference between a credible disclosure and a spreadsheet exercise.</p>
<p>Document your methodology explicitly: which categories you calculated, which method you used per category, which emission factor database and vintage you applied, and what assumptions you made on currency conversion or NAICS mapping. The GHG Protocol provides an assurance readiness checklist as part of its Scope 3 guidance.</p>
<p>Version-control your calculation code. An R script under git history is a better audit trail than an Excel file with “final_v3_FINAL.xlsx” in the name. Quarto reports render the methodology narrative and the computed results together, so there is no gap between what you documented and what you calculated. This mirrors the <a href="../../../blog/posts/esia-reproducibility/">reproducibility principles</a> we apply to environmental impact assessments.</p>
<p>Apply uncertainty bounds. Spend-based emission factors carry large uncertainty ranges, typically plus or minus 30-50%. Reporting a single point estimate without acknowledging this overstates precision. A simple Monte Carlo simulation over the factor distributions communicates the range of possible outcomes and builds credibility with sophisticated reviewers.</p>
<hr>
<p>Scope 3 accounting separates organisations that are genuinely working through their climate exposure from those producing disclosure documents for appearances. The methodology is not opaque, and the tools are not expensive. What it requires is structured procurement data, a clear methodology, and a willingness to show your work.</p>
<p>If your organisation is working through this for the first time, what is the biggest obstacle you are running into: data quality, methodology uncertainty, or internal buy-in for the process?</p>
<p>Kwiz Computing Technologies provides ESG analytics services for Kenyan and East African organisations, including GHG inventory design, reproducible Scope 3 workflows in R, and sustainability reporting support. <a href="../../../contact.html">Reach out</a> if you want to build this capability in-house or need a partner for your next climate disclosure.</p>


</section>

 ]]></description>
  <category>environmental-analytics</category>
  <category>ESG</category>
  <guid>https://kwizresearch.com/blog/posts/esg-scope3-emissions-accounting-r-kenya-companies/</guid>
  <pubDate>Thu, 23 Apr 2026 00:00:00 GMT</pubDate>
  <media:content url="https://kwizresearch.com/blog/posts/esg-scope3-emissions-accounting-r-kenya-companies/thumbnail.png" medium="image" type="image/png"/>
</item>
<item>
  <title>Data Engineering in R: ETL Pipelines That Hold</title>
  <dc:creator>Kwiz Computing Technologies</dc:creator>
  <link>https://kwizresearch.com/blog/posts/data-engineering-r-etl-pipelines-east-africa/</link>
  <description><![CDATA[ 




<p>Every data engineering conversation eventually assumes Python. The Airflow docs assume Python. The dbt tutorials assume Python. Most “modern data stack” blog posts treat Python as the only serious choice for pipeline orchestration. Your R team is left wondering whether to rewrite months of analytical work in a language chosen for its ecosystem brand rather than its fit for the problem.</p>
<p>R’s data engineering ecosystem is more capable than this framing suggests. The <code>{targets}</code> package provides DAG-based pipeline orchestration with dependency tracking and incremental execution. <code>{arrow}</code> gives you columnar storage and out-of-core processing. <code>{dm}</code> models relational schemas directly in R, enforcing foreign key constraints before anything hits the warehouse. <code>{DBI}</code> with <code>{RPostgres}</code> writes cleanly to any PostgreSQL-compatible warehouse. For analytical ETL workloads under a few hundred gigabytes, this stack matches what most Python teams build, without a rewrite.</p>
<section id="the-realistic-data-mess-m-pesa-aggregation" class="level2">
<h2 class="anchored" data-anchor-id="the-realistic-data-mess-m-pesa-aggregation">The Realistic Data Mess: M-Pesa Aggregation</h2>
<p>Consider a Nairobi-based B2B distributor that processes payments through Safaricom’s M-Pesa Bulk Disbursement API. Every morning, the finance team exports three separate data artefacts:</p>
<ul>
<li><strong>M-Pesa transaction CSVs</strong> from the Business Pay portal, one file per settlement batch, inconsistent column names across Safaricom API versions</li>
<li><strong>USSD session logs</strong> from their internal USSD shortcode, a pipe-delimited flat file recording which merchants initiated which sessions</li>
<li><strong>CRM export</strong> from their Salesforce instance, JSON-formatted, containing merchant master data including county, tier, and assigned sales rep</li>
</ul>
<p>The current process: a finance analyst manually downloads all three, pastes them into a master Excel sheet, and sends it to the sales director by 10 a.m. When the analyst is out sick, the director works blind. When two M-Pesa API versions are running simultaneously (which Safaricom does during gradual rollouts), the manual merge produces silent duplicates.</p>
<p>This is the pipeline we will build.</p>
</section>
<section id="why-targets-fits-data-engineering" class="level2">
<h2 class="anchored" data-anchor-id="why-targets-fits-data-engineering">Why {targets} Fits Data Engineering</h2>
<p><code>{targets}</code> is a Make-like pipeline orchestrator for R. Each unit of computation is a “target” defined by a name, a function, and its dependencies. When you run the pipeline, <code>{targets}</code> builds a directed acyclic graph (DAG) of all targets, checks which outputs are already up-to-date, and skips recomputation for any target whose inputs have not changed.</p>
<p>That behaviour matters for ETL. If the CRM export has not changed since yesterday’s run, <code>{targets}</code> will not re-parse and re-validate it. If only the M-Pesa CSV is new, only the targets downstream of that file get recomputed. This is incremental execution without writing any custom caching logic.</p>
<p>The entire pipeline lives in a <code>_targets.R</code> file at the project root:</p>
<div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb1-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># _targets.R</span></span>
<span id="cb1-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(targets)</span>
<span id="cb1-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(tarchetypes)</span>
<span id="cb1-4"></span>
<span id="cb1-5"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tar_option_set</span>(</span>
<span id="cb1-6">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">packages =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(</span>
<span id="cb1-7">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"dplyr"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"readr"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"jsonlite"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"arrow"</span>,</span>
<span id="cb1-8">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"DBI"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"RPostgres"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"pointblank"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"lubridate"</span></span>
<span id="cb1-9">  ),</span>
<span id="cb1-10">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Store intermediate targets as Arrow/Parquet files</span></span>
<span id="cb1-11">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">format =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"parquet"</span></span>
<span id="cb1-12">)</span>
<span id="cb1-13"></span>
<span id="cb1-14"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Source pipeline functions</span></span>
<span id="cb1-15"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tar_source</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"R/"</span>)</span>
<span id="cb1-16"></span>
<span id="cb1-17"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(</span>
<span id="cb1-18">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ── Raw ingest targets ───────────────────────────────────────────────────</span></span>
<span id="cb1-19">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tar_target</span>(</span>
<span id="cb1-20">    mpesa_raw,</span>
<span id="cb1-21">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ingest_mpesa_csvs</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"data/raw/mpesa/"</span>),</span>
<span id="cb1-22">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">format =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"parquet"</span></span>
<span id="cb1-23">  ),</span>
<span id="cb1-24">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tar_target</span>(</span>
<span id="cb1-25">    ussd_raw,</span>
<span id="cb1-26">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ingest_ussd_logs</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"data/raw/ussd/session_log.psv"</span>),</span>
<span id="cb1-27">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">format =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"parquet"</span></span>
<span id="cb1-28">  ),</span>
<span id="cb1-29">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tar_target</span>(</span>
<span id="cb1-30">    crm_raw,</span>
<span id="cb1-31">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ingest_crm_json</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"data/raw/crm/merchants.json"</span>),</span>
<span id="cb1-32">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">format =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"parquet"</span></span>
<span id="cb1-33">  ),</span>
<span id="cb1-34"></span>
<span id="cb1-35">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ── Validation targets ───────────────────────────────────────────────────</span></span>
<span id="cb1-36">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tar_target</span>(mpesa_validated,   <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">validate_mpesa</span>(mpesa_raw)),</span>
<span id="cb1-37">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tar_target</span>(ussd_validated,    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">validate_ussd</span>(ussd_raw)),</span>
<span id="cb1-38">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tar_target</span>(crm_validated,     <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">validate_crm</span>(crm_raw)),</span>
<span id="cb1-39"></span>
<span id="cb1-40">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ── Transform to canonical schema ───────────────────────────────────────</span></span>
<span id="cb1-41">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tar_target</span>(</span>
<span id="cb1-42">    transactions_canonical,</span>
<span id="cb1-43">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">build_transactions</span>(mpesa_validated, ussd_validated, crm_validated),</span>
<span id="cb1-44">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">format =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"parquet"</span></span>
<span id="cb1-45">  ),</span>
<span id="cb1-46"></span>
<span id="cb1-47">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ── Warehouse write ──────────────────────────────────────────────────────</span></span>
<span id="cb1-48">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tar_target</span>(</span>
<span id="cb1-49">    warehouse_write,</span>
<span id="cb1-50">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">write_to_warehouse</span>(transactions_canonical),</span>
<span id="cb1-51">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">format =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"rds"</span>   <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Return a status object, not the data itself</span></span>
<span id="cb1-52">  )</span>
<span id="cb1-53">)</span></code></pre></div>
<p>Run the full pipeline with <code>tar_make()</code>. Inspect the dependency graph with <code>tar_visnetwork()</code>. Check which targets are outdated with <code>tar_outdated()</code>. These three functions cover most of the operator workflow.</p>
</section>
<section id="raw-ingest-functions" class="level2">
<h2 class="anchored" data-anchor-id="raw-ingest-functions">Raw Ingest Functions</h2>
<p>The M-Pesa portal produces CSVs with shifting column names. Safaricom’s API v2 uses <code>TransactionID</code>; v3 uses <code>transaction_id</code>. The ingest function normalises both:</p>
<div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb2-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># R/ingest.R</span></span>
<span id="cb2-2"></span>
<span id="cb2-3">ingest_mpesa_csvs <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(dir_path) {</span>
<span id="cb2-4">  csv_files <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list.files</span>(dir_path, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">pattern =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">\\</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">.csv$"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">full.names =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>)</span>
<span id="cb2-5"></span>
<span id="cb2-6">  <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> (<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">length</span>(csv_files) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>) {</span>
<span id="cb2-7">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">stop</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"No M-Pesa CSV files found in: "</span>, dir_path)</span>
<span id="cb2-8">  }</span>
<span id="cb2-9"></span>
<span id="cb2-10">  purrr<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">map</span>(csv_files, <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(f) {</span>
<span id="cb2-11">    df <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> readr<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">read_csv</span>(f, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">show_col_types =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">FALSE</span>)</span>
<span id="cb2-12"></span>
<span id="cb2-13">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Normalise column names: handle v2 and v3 API schemas</span></span>
<span id="cb2-14">    df <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb2-15">      dplyr<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">rename_with</span>(tolower) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb2-16">      dplyr<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">rename_with</span>(<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">~</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">gsub</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">" "</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"_"</span>, .x)) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb2-17">      <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Coerce either naming convention to canonical names</span></span>
<span id="cb2-18">      dplyr<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">rename</span>(</span>
<span id="cb2-19">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">any_of</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(</span>
<span id="cb2-20">          <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">transaction_id    =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"transactionid"</span>,</span>
<span id="cb2-21">          <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">merchant_phone    =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"phone_number"</span>,</span>
<span id="cb2-22">          <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">amount_kes        =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"amount"</span>,</span>
<span id="cb2-23">          <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">transaction_date  =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"date"</span>,</span>
<span id="cb2-24">          <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">transaction_date  =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"transactiondate"</span></span>
<span id="cb2-25">        ))</span>
<span id="cb2-26">      ) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb2-27">      dplyr<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mutate</span>(</span>
<span id="cb2-28">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">source_file =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">basename</span>(f),</span>
<span id="cb2-29">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">ingested_at =</span> lubridate<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">now</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Africa/Nairobi"</span>)</span>
<span id="cb2-30">      )</span>
<span id="cb2-31">  }) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb2-32">    dplyr<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">bind_rows</span>()</span>
<span id="cb2-33">}</span>
<span id="cb2-34"></span>
<span id="cb2-35">ingest_ussd_logs <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(file_path) {</span>
<span id="cb2-36">  readr<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">read_delim</span>(</span>
<span id="cb2-37">    file_path,</span>
<span id="cb2-38">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">delim =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"|"</span>,</span>
<span id="cb2-39">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">col_types =</span> readr<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">cols</span>(</span>
<span id="cb2-40">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">session_id   =</span> readr<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">col_character</span>(),</span>
<span id="cb2-41">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">phone        =</span> readr<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">col_character</span>(),</span>
<span id="cb2-42">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">initiated_at =</span> readr<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">col_datetime</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">format =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"%Y-%m-%d %H:%M:%S"</span>),</span>
<span id="cb2-43">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">menu_path    =</span> readr<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">col_character</span>(),</span>
<span id="cb2-44">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">completed    =</span> readr<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">col_logical</span>()</span>
<span id="cb2-45">    )</span>
<span id="cb2-46">  ) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb2-47">    dplyr<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mutate</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">ingested_at =</span> lubridate<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">now</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Africa/Nairobi"</span>))</span>
<span id="cb2-48">}</span>
<span id="cb2-49"></span>
<span id="cb2-50">ingest_crm_json <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(file_path) {</span>
<span id="cb2-51">  raw <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> jsonlite<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">read_json</span>(file_path, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">simplifyVector =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>)</span>
<span id="cb2-52">  tibble<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as_tibble</span>(raw) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb2-53">    dplyr<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mutate</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">ingested_at =</span> lubridate<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">now</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Africa/Nairobi"</span>))</span>
<span id="cb2-54">}</span></code></pre></div>
<p>Keeping ingest functions pure (no side effects, no database calls) means <code>{targets}</code> can cache their outputs independently. If only the USSD log changes, the M-Pesa ingest result stays cached.</p>
</section>
<section id="validation-with-pointblank" class="level2">
<h2 class="anchored" data-anchor-id="validation-with-pointblank">Validation with {pointblank}</h2>
<p>Silent bad data is worse than a failed pipeline. A single duplicated <code>transaction_id</code> or a NULL <code>merchant_phone</code> can corrupt downstream aggregations without raising any errors. The validation step makes those problems loud:</p>
<div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb3-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># R/validate.R</span></span>
<span id="cb3-2"></span>
<span id="cb3-3">validate_mpesa <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(df) {</span>
<span id="cb3-4">  agent <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> pointblank<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">create_agent</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">tbl =</span> df, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">label =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"M-Pesa validation"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb3-5">    pointblank<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">col_is_character</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">vars</span>(transaction_id)) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb3-6">    pointblank<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">col_vals_not_null</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">vars</span>(transaction_id, amount_kes, merchant_phone)) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb3-7">    pointblank<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">col_vals_gt</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">vars</span>(amount_kes), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">value =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb3-8">    pointblank<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">col_vals_regex</span>(</span>
<span id="cb3-9">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">vars</span>(merchant_phone),</span>
<span id="cb3-10">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">regex =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"^2547[0-9]{8}$"</span>   <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Safaricom 07xx -&gt; 2547xx normalised</span></span>
<span id="cb3-11">    ) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb3-12">    pointblank<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">rows_distinct</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">vars</span>(transaction_id)) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb3-13">    pointblank<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">interrogate</span>()</span>
<span id="cb3-14"></span>
<span id="cb3-15">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Fail the pipeline target if critical checks do not pass</span></span>
<span id="cb3-16">  <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> (pointblank<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">all_passed</span>(agent) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">FALSE</span>) {</span>
<span id="cb3-17">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">stop</span>(</span>
<span id="cb3-18">      <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"M-Pesa validation failed. Run pointblank::get_agent_report(agent) to inspect.</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">\n</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>,</span>
<span id="cb3-19">      <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Failed checks: "</span>,</span>
<span id="cb3-20">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">paste</span>(pointblank<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">get_sundered_data</span>(agent, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"fail"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">nrow</span>(), <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"rows rejected"</span>)</span>
<span id="cb3-21">    )</span>
<span id="cb3-22">  }</span>
<span id="cb3-23"></span>
<span id="cb3-24">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Return only rows that passed all checks</span></span>
<span id="cb3-25">  pointblank<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">get_sundered_data</span>(agent, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"pass"</span>)</span>
<span id="cb3-26">}</span></code></pre></div>
<p>The same pattern applies to USSD and CRM targets. Failed validation stops the pipeline at the right point, before bad data reaches the warehouse.</p>
</section>
<section id="arrowparquet-for-intermediate-storage" class="level2">
<h2 class="anchored" data-anchor-id="arrowparquet-for-intermediate-storage">Arrow/Parquet for Intermediate Storage</h2>
<p>The <code>format = "parquet"</code> argument in <code>tar_option_set()</code> tells <code>{targets}</code> to serialise each target’s output as a Parquet file rather than an RDS object. For a few million M-Pesa rows, this matters: Parquet files are 5-10x smaller than equivalent RDS files, read faster with columnar access patterns, and can be queried directly by DuckDB or shared with Python colleagues without conversion.</p>
<p>For targets that need to cross the R-to-R boundary within the pipeline, this is transparent. <code>{targets}</code> reads and writes Parquet automatically using <code>{arrow}</code>. For targets that need further processing before the warehouse write, you can work with Arrow tables directly:</p>
<div class="sourceCode" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb4-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># R/transform.R</span></span>
<span id="cb4-2"></span>
<span id="cb4-3">build_transactions <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(mpesa, ussd, crm) {</span>
<span id="cb4-4">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Join M-Pesa transactions with USSD session data on merchant phone</span></span>
<span id="cb4-5">  transactions <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> mpesa <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb4-6">    dplyr<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">left_join</span>(</span>
<span id="cb4-7">      ussd <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span> dplyr<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">select</span>(phone, session_id, initiated_at, completed),</span>
<span id="cb4-8">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">by =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"merchant_phone"</span> <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"phone"</span>),</span>
<span id="cb4-9">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">relationship =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"many-to-one"</span></span>
<span id="cb4-10">    ) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb4-11">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Enrich with CRM merchant data</span></span>
<span id="cb4-12">    dplyr<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">left_join</span>(</span>
<span id="cb4-13">      crm <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span> dplyr<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">select</span>(phone, merchant_name, county, tier, sales_rep),</span>
<span id="cb4-14">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">by =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"merchant_phone"</span> <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"phone"</span>)</span>
<span id="cb4-15">    ) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb4-16">    dplyr<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mutate</span>(</span>
<span id="cb4-17">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">transaction_date =</span> lubridate<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as_date</span>(transaction_date),</span>
<span id="cb4-18">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">week_start       =</span> lubridate<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">floor_date</span>(transaction_date, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"week"</span>),</span>
<span id="cb4-19">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">county           =</span> dplyr<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">coalesce</span>(county, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Unknown"</span>),</span>
<span id="cb4-20">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">amount_kes       =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.numeric</span>(amount_kes)</span>
<span id="cb4-21">    ) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb4-22">    dplyr<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">select</span>(</span>
<span id="cb4-23">      transaction_id, transaction_date, week_start,</span>
<span id="cb4-24">      merchant_phone, merchant_name, county, tier, sales_rep,</span>
<span id="cb4-25">      amount_kes, session_id, completed, source_file, ingested_at</span>
<span id="cb4-26">    )</span>
<span id="cb4-27"></span>
<span id="cb4-28">  transactions</span>
<span id="cb4-29">}</span></code></pre></div>
</section>
<section id="writing-to-postgresql-with-dbi" class="level2">
<h2 class="anchored" data-anchor-id="writing-to-postgresql-with-dbi">Writing to PostgreSQL with DBI</h2>
<p>The warehouse write target connects to PostgreSQL and upserts the canonical transactions table. The pattern uses <code>DBI::dbWriteTable()</code> with a staging table for idempotent loads: write the new batch to a staging table, then merge into the production table on <code>transaction_id</code>. That way, re-running the pipeline after a partial failure does not create duplicates.</p>
<div class="sourceCode" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb5-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># R/warehouse.R</span></span>
<span id="cb5-2"></span>
<span id="cb5-3">write_to_warehouse <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(transactions) {</span>
<span id="cb5-4">  con <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> DBI<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">dbConnect</span>(</span>
<span id="cb5-5">    RPostgres<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">Postgres</span>(),</span>
<span id="cb5-6">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">host     =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">Sys.getenv</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"PG_HOST"</span>),</span>
<span id="cb5-7">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">port     =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.integer</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">Sys.getenv</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"PG_PORT"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"5432"</span>)),</span>
<span id="cb5-8">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">dbname   =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">Sys.getenv</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"PG_DB"</span>),</span>
<span id="cb5-9">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">user     =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">Sys.getenv</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"PG_USER"</span>),</span>
<span id="cb5-10">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">password =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">Sys.getenv</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"PG_PASSWORD"</span>)</span>
<span id="cb5-11">  )</span>
<span id="cb5-12">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">on.exit</span>(DBI<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">dbDisconnect</span>(con), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">add =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>)</span>
<span id="cb5-13"></span>
<span id="cb5-14">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Write to staging table (overwrite on each run)</span></span>
<span id="cb5-15">  DBI<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">dbWriteTable</span>(</span>
<span id="cb5-16">    con,</span>
<span id="cb5-17">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">name      =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"mpesa_transactions_staging"</span>,</span>
<span id="cb5-18">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">value     =</span> transactions,</span>
<span id="cb5-19">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">overwrite =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>,</span>
<span id="cb5-20">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">row.names =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">FALSE</span></span>
<span id="cb5-21">  )</span>
<span id="cb5-22"></span>
<span id="cb5-23">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Upsert from staging to production table</span></span>
<span id="cb5-24">  DBI<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">dbExecute</span>(con, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span></span>
<span id="cb5-25"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    INSERT INTO mpesa_transactions (</span></span>
<span id="cb5-26"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      transaction_id, transaction_date, week_start,</span></span>
<span id="cb5-27"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      merchant_phone, merchant_name, county, tier, sales_rep,</span></span>
<span id="cb5-28"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      amount_kes, session_id, completed, source_file, ingested_at</span></span>
<span id="cb5-29"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    )</span></span>
<span id="cb5-30"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    SELECT</span></span>
<span id="cb5-31"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      transaction_id, transaction_date, week_start,</span></span>
<span id="cb5-32"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      merchant_phone, merchant_name, county, tier, sales_rep,</span></span>
<span id="cb5-33"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      amount_kes, session_id, completed, source_file, ingested_at</span></span>
<span id="cb5-34"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    FROM mpesa_transactions_staging</span></span>
<span id="cb5-35"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    ON CONFLICT (transaction_id)</span></span>
<span id="cb5-36"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    DO UPDATE SET</span></span>
<span id="cb5-37"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      amount_kes   = EXCLUDED.amount_kes,</span></span>
<span id="cb5-38"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      ingested_at  = EXCLUDED.ingested_at</span></span>
<span id="cb5-39"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  "</span>)</span>
<span id="cb5-40"></span>
<span id="cb5-41">  rows_written <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> DBI<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">dbGetQuery</span>(</span>
<span id="cb5-42">    con,</span>
<span id="cb5-43">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"SELECT COUNT(*) AS n FROM mpesa_transactions_staging"</span></span>
<span id="cb5-44">  )<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>n</span>
<span id="cb5-45"></span>
<span id="cb5-46">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">status =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"ok"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">rows =</span> rows_written, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">timestamp =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">Sys.time</span>())</span>
<span id="cb5-47">}</span></code></pre></div>
<p>The function returns a small status list rather than the full dataset. <code>{targets}</code> stores this as an RDS file, keeping the cache small and the pipeline log readable.</p>
</section>
<section id="scheduling-cron-and-github-actions" class="level2">
<h2 class="anchored" data-anchor-id="scheduling-cron-and-github-actions">Scheduling: Cron and GitHub Actions</h2>
<p>The pipeline runs daily. Two scheduling options work cleanly with this <code>{targets}</code> setup.</p>
<p><strong>Cron on a Linux server:</strong> A crontab entry calls an R script that sets environment variables from a <code>.Renviron</code> file and runs <code>tar_make()</code>:</p>
<div class="sourceCode" id="cb6" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb6-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Run pipeline Monday-Friday at 07:00 Nairobi time (EAT = UTC+3)</span></span>
<span id="cb6-2"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">0</span> 4 <span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">*</span> <span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">*</span> 1-5 /usr/bin/Rscript /opt/pipelines/mpesa-etl/run_pipeline.R <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;&gt;</span> /var/log/mpesa-etl.log <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;&amp;</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span></span></code></pre></div>
<p>The <code>run_pipeline.R</code> script:</p>
<div class="sourceCode" id="cb7" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb7-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#!/usr/bin/env Rscript</span></span>
<span id="cb7-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">readRenviron</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"/opt/pipelines/.Renviron"</span>)</span>
<span id="cb7-3">targets<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tar_make</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">reporter =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"timestamp"</span>)</span></code></pre></div>
<p><strong>GitHub Actions:</strong> For teams that store the pipeline in Git (recommended), a workflow file handles scheduling and sends failures to a Slack webhook:</p>
<div class="sourceCode" id="cb8" style="background: #f1f3f5;"><pre class="sourceCode yaml code-with-copy"><code class="sourceCode yaml"><span id="cb8-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">name</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> M-Pesa ETL Pipeline</span></span>
<span id="cb8-2"></span>
<span id="cb8-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">on</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb8-4"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">schedule</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb8-5"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">cron</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"0 4 * * 1-5"</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">   # 07:00 EAT, weekdays</span></span>
<span id="cb8-6"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">workflow_dispatch</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">          # Allow manual trigger</span></span>
<span id="cb8-7"></span>
<span id="cb8-8"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">jobs</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb8-9"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">run-pipeline</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb8-10"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">runs-on</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> ubuntu-latest</span></span>
<span id="cb8-11"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">steps</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb8-12"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">uses</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> actions/checkout@v4</span></span>
<span id="cb8-13"></span>
<span id="cb8-14"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">uses</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> r-lib/actions/setup-r@v2</span></span>
<span id="cb8-15"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">with</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb8-16"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">          </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">r-version</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"4.4"</span></span>
<span id="cb8-17"></span>
<span id="cb8-18"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">name</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> Restore targets cache</span></span>
<span id="cb8-19"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">uses</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> actions/cache@v4</span></span>
<span id="cb8-20"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">with</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb8-21"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">          </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">path</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> _targets/</span></span>
<span id="cb8-22"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">          </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">key</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> targets-${{ hashFiles('_targets.R', 'R/**') }}</span></span>
<span id="cb8-23"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">          </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">restore-keys</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> targets-</span></span>
<span id="cb8-24"></span>
<span id="cb8-25"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">name</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> Run pipeline</span></span>
<span id="cb8-26"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">env</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb8-27"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">          </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">PG_HOST</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">     ${{ secrets.PG_HOST }}</span></span>
<span id="cb8-28"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">          </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">PG_DB</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">       ${{ secrets.PG_DB }}</span></span>
<span id="cb8-29"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">          </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">PG_USER</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">     ${{ secrets.PG_USER }}</span></span>
<span id="cb8-30"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">          </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">PG_PASSWORD</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> ${{ secrets.PG_PASSWORD }}</span></span>
<span id="cb8-31"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">run</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> Rscript -e "targets::tar_make(reporter = 'timestamp')"</span></span>
<span id="cb8-32"></span>
<span id="cb8-33"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">name</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> Notify on failure</span></span>
<span id="cb8-34"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">if</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> failure()</span></span>
<span id="cb8-35"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">uses</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> slackapi/slack-github-action@v1</span></span>
<span id="cb8-36"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">with</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb8-37"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">          </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">payload</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'{"text": "M-Pesa ETL failed. Check the Actions log."}'</span></span>
<span id="cb8-38"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">env</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb8-39"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">          </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">SLACK_WEBHOOK_URL</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> ${{ secrets.SLACK_WEBHOOK }}</span></span></code></pre></div>
<p>The <code>actions/cache</code> step restores the <code>_targets/</code> store between runs. GitHub Actions only re-runs targets whose inputs changed, because the cached store persists across workflow runs. For a pipeline that ingests daily increments, this means only the new M-Pesa batch gets processed on most days.</p>
</section>
<section id="error-handling-and-observability" class="level2">
<h2 class="anchored" data-anchor-id="error-handling-and-observability">Error Handling and Observability</h2>
<p><code>{targets}</code> surfaces errors at the target level. When <code>validate_mpesa</code> throws, the pipeline stops there, the error message goes to stdout (and to the Actions log), and all downstream targets are marked as failed. The CRM and USSD targets, which have no dependency on <code>mpesa_validated</code>, still succeed and get cached.</p>
<p>For production alerting, wrap <code>tar_make()</code> in a tryCatch and notify on failure:</p>
<div class="sourceCode" id="cb9" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb9-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># run_pipeline.R</span></span>
<span id="cb9-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">readRenviron</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">".Renviron"</span>)</span>
<span id="cb9-3"></span>
<span id="cb9-4"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tryCatch</span>(</span>
<span id="cb9-5">  {</span>
<span id="cb9-6">    targets<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tar_make</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">reporter =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"timestamp"</span>)</span>
<span id="cb9-7">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">message</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Pipeline completed: "</span>, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">Sys.time</span>())</span>
<span id="cb9-8">  },</span>
<span id="cb9-9">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">error =</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(e) {</span>
<span id="cb9-10">    msg <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">paste0</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"ETL pipeline failed at "</span>, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">Sys.time</span>(), <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">\n</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">conditionMessage</span>(e))</span>
<span id="cb9-11">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">message</span>(msg)</span>
<span id="cb9-12">    httr2<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">request</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">Sys.getenv</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"SLACK_WEBHOOK"</span>)) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb9-13">      httr2<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">req_body_json</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">text =</span> msg)) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb9-14">      httr2<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">req_perform</span>()</span>
<span id="cb9-15">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">quit</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">status =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb9-16">  }</span>
<span id="cb9-17">)</span></code></pre></div>
<p>This pattern keeps the scheduling layer thin. Whether you use cron, GitHub Actions, or a task runner like <code>{rrq}</code>, the pipeline itself handles error propagation through the DAG.</p>
</section>
<section id="what-this-solves-in-practice" class="level2">
<h2 class="anchored" data-anchor-id="what-this-solves-in-practice">What This Solves in Practice</h2>
<p>The Nairobi distributor scenario from the opening is representative of dozens of companies operating at this scale across East Africa: telecom payment data, field agent apps generating USSD logs, and CRM systems that do not talk to each other. The data volumes sit comfortably below 100 GB. The analytical questions (weekly revenue by county, merchant tier performance, sales rep attribution) do not require Spark.</p>
<p>What they require is a pipeline that runs reliably, fails loudly, and does not need a Python engineer to maintain. The <code>{targets}</code> stack delivers that. The finance analyst stops building spreadsheets at 9:45 a.m. The dashboard updates itself. The sales director gets county-level M-Pesa attribution by the time the morning stand-up starts.</p>
<p>A few practical notes from running this pattern with clients in Nairobi and Mombasa. First, store the <code>_targets/</code> directory on a mounted volume if you run in Docker; it is your cache and you do not want it wiped on container restart. Second, the <code>{dm}</code> package fits naturally between the transform and warehouse-write steps: define your relational model once, use <code>dm_examine_constraints()</code> to catch foreign key violations before the upsert, and you get an extra layer of guarantees without writing SQL assertions by hand. Third, run <code>tar_prune()</code> monthly to clean up cached targets that are no longer referenced by <code>_targets.R</code>; the <code>_targets/objects/</code> directory grows faster than most teams expect.</p>
<p>If you are managing heterogeneous data sources at this scale, the Python assumption may be costing you more than you realise. The R tooling is ready. The question is whether your architecture is.</p>
<p>For teams that want help designing or reviewing a pipeline like this, <a href="../../..">Kwiz Computing</a> builds data engineering systems for East African businesses across sectors. We also cover how these pipelines connect to production R deployments in our <a href="../../../blog/posts/r-for-business-leaders/">business leaders guide to R</a> and the <a href="../../../blog/posts/rshiny-hosting-guide/">R Shiny hosting guide</a> for teams that expose pipeline outputs as interactive dashboards.</p>
<p>What part of your current pipeline breaks most often: ingest, validation, or the warehouse write?</p>


</section>

 ]]></description>
  <category>enterprise-data-science</category>
  <category>data-engineering</category>
  <guid>https://kwizresearch.com/blog/posts/data-engineering-r-etl-pipelines-east-africa/</guid>
  <pubDate>Thu, 23 Apr 2026 00:00:00 GMT</pubDate>
  <media:content url="https://kwizresearch.com/blog/posts/data-engineering-r-etl-pipelines-east-africa/thumbnail.png" medium="image" type="image/png"/>
</item>
<item>
  <title>EIA Biodiversity R: Automate NEMA Inventory Tables</title>
  <dc:creator>Kwiz Computing Technologies</dc:creator>
  <link>https://kwizresearch.com/blog/posts/reproducible-eia-baseline-reports-r-nema-biodiversity-inventory-automation/</link>
  <description><![CDATA[ 




<p>It is 1:47 am the night before your NEMA submission and you are rebuilding the Species Inventory Annex because a field team updated their transect counts after you had already formatted the table. The new data changes three species totals, which means the richness summary in Chapter 4 is now wrong, the IUCN threat-status flag in Annex C still references the old count, and the species accumulation figure no longer matches either. You are not making an analytical error. You are paying the price for a workflow that treats biodiversity data as a formatting problem rather than a data problem.</p>
<p>This post is about fixing that workflow. The fix is a single canonical R tibble that holds all field biodiversity data, with every NEMA-required annex table, every richness metric, and every threat-status flag derived automatically from that one source. When field data changes, you re-render. The entire annex regenerates in under two minutes.</p>
<section id="what-nema-actually-requires" class="level2">
<h2 class="anchored" data-anchor-id="what-nema-actually-requires">What NEMA Actually Requires</h2>
<p>Kenya’s Environmental Impact Assessment and Audit Regulations (Legal Notice 101 of 2003, with subsequent amendments under EMCA) require a biodiversity baseline in every EIA study report. The regulations specify that the report must include a flora and fauna inventory, a description of habitat types, identification of rare or endangered species, and a statement on endemism.</p>
<p>What the regulations do not specify is any data format, column structure, or table layout. Every firm invents its own annex template. A NEMA review officer seeing your report may have reviewed three other EIAs that week, each with a completely different table structure. Inconsistencies between your species count in the narrative and the total in the annex are easy to introduce and easy for a reviewer to spot.</p>
<p>The practical requirement, inferred from review rejections and NEMA guidance notes, is: a complete species inventory with family and order, a habitat association column, IUCN Red List category for each species, a flag for endemic or near-endemic taxa, and observation counts or presence-absence by survey zone. That structure maps directly to a tidy R tibble.</p>
</section>
<section id="designing-the-canonical-tibble" class="level2">
<h2 class="anchored" data-anchor-id="designing-the-canonical-tibble">Designing the Canonical Tibble</h2>
<p>The data model is the foundation. Every downstream table, summary, and figure reads from this one object. The columns that matter for NEMA compliance are:</p>
<div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb1-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(dplyr)</span>
<span id="cb1-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(tibble)</span>
<span id="cb1-3"></span>
<span id="cb1-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Minimal canonical schema for NEMA biodiversity inventory</span></span>
<span id="cb1-5">species_data <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tibble</span>(</span>
<span id="cb1-6">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Taxonomy</span></span>
<span id="cb1-7">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">species        =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">character</span>(),   <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Accepted binomial</span></span>
<span id="cb1-8">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">common_name    =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">character</span>(),</span>
<span id="cb1-9">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">family         =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">character</span>(),</span>
<span id="cb1-10">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">order          =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">character</span>(),</span>
<span id="cb1-11">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">class          =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">character</span>(),   <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Aves, Mammalia, Reptilia, etc.</span></span>
<span id="cb1-12"></span>
<span id="cb1-13">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Spatial and habitat context</span></span>
<span id="cb1-14">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">habitat        =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">character</span>(),   <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Riparian, Savanna, Woodland, etc.</span></span>
<span id="cb1-15">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">survey_zone    =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">character</span>(),   <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># e.g. "Zone A – Project Footprint"</span></span>
<span id="cb1-16">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">observation_count =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">integer</span>(),</span>
<span id="cb1-17">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">detection_method =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">character</span>(), <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Transect, Mist-net, Camera-trap, etc.</span></span>
<span id="cb1-18"></span>
<span id="cb1-19">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Conservation status</span></span>
<span id="cb1-20">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">iucn_category  =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">character</span>(),   <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># LC, NT, VU, EN, CR, EW, EX, DD, NE</span></span>
<span id="cb1-21">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">cites_appendix =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">character</span>(),   <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># I, II, III, or NA</span></span>
<span id="cb1-22">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">endemic        =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">logical</span>(),     <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># TRUE if Kenya endemic or near-endemic</span></span>
<span id="cb1-23">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">protected_under_wcma =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">logical</span>(), <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Wildlife Conservation &amp; Management Act</span></span>
<span id="cb1-24"></span>
<span id="cb1-25">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Data provenance</span></span>
<span id="cb1-26">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">observer       =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">character</span>(),</span>
<span id="cb1-27">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">survey_date    =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.Date</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">character</span>()),</span>
<span id="cb1-28">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">record_id      =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">character</span>()    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Unique key for each observation event</span></span>
<span id="cb1-29">)</span></code></pre></div>
<p>Build this once per project, populate it from your field datasheets, and never touch it manually again. If a field team revises their counts, they edit the source CSV. The tibble reads from that CSV. Every downstream product regenerates.</p>
<p>For actual project use, read the canonical tibble from a version-controlled CSV:</p>
<div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb2-1">species_data <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> readr<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">read_csv</span>(</span>
<span id="cb2-2">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"data/raw/biodiversity_field_records.csv"</span>,</span>
<span id="cb2-3">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">col_types =</span> readr<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">cols</span>(</span>
<span id="cb2-4">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">survey_date =</span> readr<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">col_date</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">format =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"%Y-%m-%d"</span>),</span>
<span id="cb2-5">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">endemic =</span> readr<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">col_logical</span>(),</span>
<span id="cb2-6">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">protected_under_wcma =</span> readr<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">col_logical</span>(),</span>
<span id="cb2-7">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">observation_count =</span> readr<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">col_integer</span>()</span>
<span id="cb2-8">  )</span>
<span id="cb2-9">)</span></code></pre></div>
</section>
<section id="iucn-red-list-status-lookup-without-manual-copying" class="level2">
<h2 class="anchored" data-anchor-id="iucn-red-list-status-lookup-without-manual-copying">IUCN Red List Status: Lookup Without Manual Copying</h2>
<p>Manually typing IUCN categories is where errors accumulate fastest. The <code>rredlist</code> package provides programmatic access to the IUCN Red List API. For field projects in Kenya, you typically work with a bounded species list, so a cached lookup makes more sense than live API calls during report rendering.</p>
<div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb3-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(rredlist)</span>
<span id="cb3-2"></span>
<span id="cb3-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># One-time lookup: run this when species list is finalised</span></span>
<span id="cb3-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Requires IUCN_REDLIST_KEY environment variable</span></span>
<span id="cb3-5">fetch_iucn_status <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(species_list, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">cache_path =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"data/iucn_cache.rds"</span>) {</span>
<span id="cb3-6"></span>
<span id="cb3-7">  <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> (<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">file.exists</span>(cache_path)) {</span>
<span id="cb3-8">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">return</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">readRDS</span>(cache_path))</span>
<span id="cb3-9">  }</span>
<span id="cb3-10"></span>
<span id="cb3-11">  results <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> purrr<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">map_dfr</span>(species_list, <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(sp) {</span>
<span id="cb3-12">    resp <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tryCatch</span>(</span>
<span id="cb3-13">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">rl_search</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">name =</span> sp)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>result,</span>
<span id="cb3-14">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">error =</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(e) <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">NULL</span></span>
<span id="cb3-15">    )</span>
<span id="cb3-16">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> (<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">is.null</span>(resp) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">||</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">nrow</span>(resp) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>) {</span>
<span id="cb3-17">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">return</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tibble</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">species =</span> sp, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">iucn_category =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"NE"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">iucn_id =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">NA_integer_</span>))</span>
<span id="cb3-18">    }</span>
<span id="cb3-19">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tibble</span>(</span>
<span id="cb3-20">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">species       =</span> sp,</span>
<span id="cb3-21">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">iucn_category =</span> resp<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>category[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>],</span>
<span id="cb3-22">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">iucn_id       =</span> resp<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>taxonid[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]</span>
<span id="cb3-23">    )</span>
<span id="cb3-24">  })</span>
<span id="cb3-25"></span>
<span id="cb3-26">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">saveRDS</span>(results, cache_path)</span>
<span id="cb3-27">  results</span>
<span id="cb3-28">}</span>
<span id="cb3-29"></span>
<span id="cb3-30">iucn_lookup <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">fetch_iucn_status</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">unique</span>(species_data<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>species))</span>
<span id="cb3-31"></span>
<span id="cb3-32"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Join back to canonical tibble</span></span>
<span id="cb3-33">species_data <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> species_data <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb3-34">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">select</span>(<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>iucn_category) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb3-35">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">left_join</span>(iucn_lookup, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">by =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"species"</span>)</span></code></pre></div>
<p>The cache at <code>data/iucn_cache.rds</code> persists across renders. When the IUCN updates a species’ status, you delete the cache file, re-run once, and the new status propagates to every table automatically. This approach also works offline once the cache exists, which matters on field trips with intermittent connectivity.</p>
<p>For projects where the species list is stable and well-known, a hand-curated lookup table stored as a CSV in <code>data/reference/iucn_status.csv</code> is a legitimate alternative. The critical point is that the status column in your canonical tibble is always derived, never typed by hand into the annex.</p>
</section>
<section id="species-richness-and-diversity-indices" class="level2">
<h2 class="anchored" data-anchor-id="species-richness-and-diversity-indices">Species Richness and Diversity Indices</h2>
<p>NEMA reviewers expect a summary of species richness by taxonomic group and habitat. Diversity indices (Shannon-Wiener and Simpson) add methodological weight and are standard in peer-reviewed baseline assessments across Kenya’s infrastructure corridor projects.</p>
<div class="sourceCode" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb4-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(vegan)</span>
<span id="cb4-2"></span>
<span id="cb4-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Species richness by class and habitat</span></span>
<span id="cb4-4">richness_summary <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> species_data <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb4-5">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">group_by</span>(class, habitat) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb4-6">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">summarise</span>(</span>
<span id="cb4-7">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">species_richness =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">n_distinct</span>(species),</span>
<span id="cb4-8">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">total_observations =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sum</span>(observation_count, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">na.rm =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>),</span>
<span id="cb4-9">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">threatened_spp =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sum</span>(iucn_category <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%in%</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"VU"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"EN"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"CR"</span>), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">na.rm =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>),</span>
<span id="cb4-10">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">endemic_spp =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sum</span>(endemic, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">na.rm =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>),</span>
<span id="cb4-11">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">.groups =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"drop"</span></span>
<span id="cb4-12">  ) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb4-13">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">arrange</span>(class, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">desc</span>(species_richness))</span>
<span id="cb4-14"></span>
<span id="cb4-15"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Shannon-Wiener and Simpson indices per habitat</span></span>
<span id="cb4-16">diversity_by_habitat <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> species_data <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb4-17">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">group_by</span>(habitat, species) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb4-18">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">summarise</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">n =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sum</span>(observation_count, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">na.rm =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">.groups =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"drop"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb4-19">  tidyr<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">pivot_wider</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">names_from =</span> species, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">values_from =</span> n, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">values_fill =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb4-20">  tibble<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">column_to_rownames</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"habitat"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb4-21">  (\(m) {</span>
<span id="cb4-22">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tibble</span>(</span>
<span id="cb4-23">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">habitat  =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">rownames</span>(m),</span>
<span id="cb4-24">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">H_shannon =</span> vegan<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">diversity</span>(m, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">index =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"shannon"</span>),</span>
<span id="cb4-25">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">D_simpson =</span> vegan<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">diversity</span>(m, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">index =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"simpson"</span>),</span>
<span id="cb4-26">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">S_richness =</span> vegan<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">specnumber</span>(m)</span>
<span id="cb4-27">    )</span>
<span id="cb4-28">  })()</span></code></pre></div>
<p>The <code>vegan</code> package computes these indices from a species-by-site matrix. The pipeline above builds that matrix directly from the canonical tibble, so the indices update automatically when field data changes.</p>
</section>
<section id="generating-the-nema-annex-tables" class="level2">
<h2 class="anchored" data-anchor-id="generating-the-nema-annex-tables">Generating the NEMA Annex Tables</h2>
<p>The annex tables are where a reproducible workflow delivers the most visible return. A formatted <code>knitr::kable()</code> table with <code>kableExtra</code> styling produces output that matches what NEMA reviewers expect, derived entirely from the canonical tibble.</p>
<div class="sourceCode" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb5-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(knitr)</span>
<span id="cb5-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(kableExtra)</span>
<span id="cb5-3"></span>
<span id="cb5-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Annex Table A: Complete Species Inventory</span></span>
<span id="cb5-5">annex_inventory <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> species_data <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb5-6">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">arrange</span>(class, order, family, species) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb5-7">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mutate</span>(</span>
<span id="cb5-8">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">iucn_display =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">case_when</span>(</span>
<span id="cb5-9">      iucn_category <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"CR"</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">~</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"CR*"</span>,</span>
<span id="cb5-10">      iucn_category <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"EN"</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">~</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"EN*"</span>,</span>
<span id="cb5-11">      iucn_category <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"VU"</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">~</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"VU*"</span>,</span>
<span id="cb5-12">      <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">~</span> iucn_category</span>
<span id="cb5-13">    ),</span>
<span id="cb5-14">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">endemic_display =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">if_else</span>(endemic, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Yes"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"No"</span>)</span>
<span id="cb5-15">  ) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb5-16">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">select</span>(</span>
<span id="cb5-17">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">Class</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span> <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">=</span> class,</span>
<span id="cb5-18">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">Order</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span> <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">=</span> order,</span>
<span id="cb5-19">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">Family</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span> <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">=</span> family,</span>
<span id="cb5-20">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">Species</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span> <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">=</span> species,</span>
<span id="cb5-21">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">Common Name</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span> <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">=</span> common_name,</span>
<span id="cb5-22">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">Habitat</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span> <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">=</span> habitat,</span>
<span id="cb5-23">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">IUCN Status</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span> <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">=</span> iucn_display,</span>
<span id="cb5-24">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">Endemic</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span> <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">=</span> endemic_display,</span>
<span id="cb5-25">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">Detections</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span> <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">=</span> observation_count,</span>
<span id="cb5-26">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">Method</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span> <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">=</span> detection_method</span>
<span id="cb5-27">  )</span>
<span id="cb5-28"></span>
<span id="cb5-29">annex_inventory <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb5-30">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">kable</span>(</span>
<span id="cb5-31">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">caption =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">paste0</span>(</span>
<span id="cb5-32">      <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Annex A: Biodiversity Species Inventory: "</span>,</span>
<span id="cb5-33">      params<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>project_name,</span>
<span id="cb5-34">      <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">" (Survey Period: "</span>, params<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>survey_period, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">")"</span></span>
<span id="cb5-35">    ),</span>
<span id="cb5-36">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">booktabs =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>,</span>
<span id="cb5-37">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">longtable =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span></span>
<span id="cb5-38">  ) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb5-39">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">kable_styling</span>(</span>
<span id="cb5-40">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">latex_options =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"repeat_header"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"striped"</span>),</span>
<span id="cb5-41">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">font_size =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">9</span></span>
<span id="cb5-42">  ) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb5-43">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">footnote</span>(</span>
<span id="cb5-44">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">general =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"* Threatened species (CR: Critically Endangered, EN: Endangered, VU: Vulnerable). IUCN Red List categories per IUCN (2024). Endemic status per Kenya Biodiversity Atlas."</span>,</span>
<span id="cb5-45">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">threeparttable =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span></span>
<span id="cb5-46">  )</span></code></pre></div>
<p>A second table summarises richness and threat status, which typically appears in the main report body rather than the annex:</p>
<div class="sourceCode" id="cb6" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb6-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Table in main report: Richness and threat status summary</span></span>
<span id="cb6-2">richness_summary <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb6-3">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">kable</span>(</span>
<span id="cb6-4">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">col.names =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Class"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Habitat"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Species Richness"</span>,</span>
<span id="cb6-5">                  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Total Detections"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Threatened Spp."</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Endemic Spp."</span>),</span>
<span id="cb6-6">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">caption =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Table 4.X: Species Richness and Conservation Status Summary by Taxonomic Class and Habitat Type"</span>,</span>
<span id="cb6-7">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">booktabs =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span></span>
<span id="cb6-8">  ) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb6-9">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">kable_styling</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">latex_options =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"striped"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb6-10">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">column_spec</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">bold =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">color =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"darkred"</span>)</span></code></pre></div>
<p>Both tables are generated from the same object. If you change a field record in <code>biodiversity_field_records.csv</code>, both update on the next render.</p>
</section>
<section id="parameterising-for-multi-project-reuse" class="level2">
<h2 class="anchored" data-anchor-id="parameterising-for-multi-project-reuse">Parameterising for Multi-Project Reuse</h2>
<p>Kenya’s infrastructure pipeline means many consultancies run concurrent EIAs. The LAPSSET corridor alone involves environmental studies across Isiolo, Marsabit, and Lamu counties. SGR Phase 2 overlaps with sensitive savanna habitat across the Naivasha-Kisumu alignment. A single parameterised template handles all of them.</p>
<div class="sourceCode" id="cb7" style="background: #f1f3f5;"><pre class="sourceCode yaml code-with-copy"><code class="sourceCode yaml"><span id="cb7-1"><span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">---</span></span>
<span id="cb7-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">title</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Biodiversity Baseline Annex: `r params$project_name`"</span></span>
<span id="cb7-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">subtitle</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"NEMA Ref: `r params$nema_ref`"</span></span>
<span id="cb7-4"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">format</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb7-5"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">pdf</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb7-6"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">toc</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">false</span></span>
<span id="cb7-7"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">number-sections</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">false</span></span>
<span id="cb7-8"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">params</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb7-9"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">project_name</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Default Project"</span></span>
<span id="cb7-10"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">nema_ref</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"NEMA/EIA/5/2/XXXX/XXX"</span></span>
<span id="cb7-11"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">survey_period</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Q1 2026"</span></span>
<span id="cb7-12"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">project_county</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Nairobi"</span></span>
<span id="cb7-13"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">project_coords</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"1.2921° S, 36.8219° E"</span></span>
<span id="cb7-14"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">field_data_path</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"data/raw/biodiversity_field_records.csv"</span></span>
<span id="cb7-15"><span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">---</span></span></code></pre></div>
<p>Render for each project from the command line:</p>
<div class="sourceCode" id="cb8" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb8-1">quarto<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">quarto_render</span>(</span>
<span id="cb8-2">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">input =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"biodiversity_annex.qmd"</span>,</span>
<span id="cb8-3">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">execute_params =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(</span>
<span id="cb8-4">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">project_name    =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"LAPSSET Port Access Road EIA"</span>,</span>
<span id="cb8-5">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">nema_ref        =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"NEMA/EIA/5/2/2026/047"</span>,</span>
<span id="cb8-6">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">survey_period   =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Q4 2025"</span>,</span>
<span id="cb8-7">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">project_county  =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Lamu"</span>,</span>
<span id="cb8-8">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">project_coords  =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"2.2717° S, 40.9020° E"</span>,</span>
<span id="cb8-9">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">field_data_path =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"data/raw/lapsset_road_biodiversity.csv"</span></span>
<span id="cb8-10">  ),</span>
<span id="cb8-11">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">output_file =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"output/LAPSSET_biodiversity_annex_2026.pdf"</span></span>
<span id="cb8-12">)</span></code></pre></div>
<p>The template reads the field data from <code>params$field_data_path</code>, runs the IUCN lookup, computes richness, and renders the full annex. New project, new CSV, same template. The table structure stays consistent across submissions.</p>
</section>
<section id="cross-checking-field-records-against-gbif" class="level2">
<h2 class="anchored" data-anchor-id="cross-checking-field-records-against-gbif">Cross-Checking Field Records Against GBIF</h2>
<p>Field surveys have finite duration and effort. A useful quality control step is to cross-reference your inventory against GBIF occurrence records for the project area, flagging species present in GBIF that your survey did not record. This does not invalidate your baseline, but it documents the comparison and gives reviewers confidence that your team considered the broader evidence base.</p>
<div class="sourceCode" id="cb9" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb9-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(rgbif)</span>
<span id="cb9-2"></span>
<span id="cb9-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Pull GBIF records within project bounding box</span></span>
<span id="cb9-4">gbif_crosscheck <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(lat_centre, lon_centre, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">radius_km =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>) {</span>
<span id="cb9-5">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">occ_search</span>(</span>
<span id="cb9-6">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">country     =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"KE"</span>,</span>
<span id="cb9-7">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">decimalLatitude  =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">paste0</span>(lat_centre <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.1</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">","</span>, lat_centre <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.1</span>),</span>
<span id="cb9-8">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">decimalLongitude =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">paste0</span>(lon_centre <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.1</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">","</span>, lon_centre <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.1</span>),</span>
<span id="cb9-9">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">hasCoordinate =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>,</span>
<span id="cb9-10">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">limit =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5000</span></span>
<span id="cb9-11">  )<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>data <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb9-12">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">select</span>(species, class, family, year, basisOfRecord) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb9-13">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">filter</span>(<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">is.na</span>(species), year <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2015</span>)</span>
<span id="cb9-14">}</span>
<span id="cb9-15"></span>
<span id="cb9-16">gbif_area <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">gbif_crosscheck</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">lat_centre =</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.29</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">lon_centre =</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">36.82</span>)</span>
<span id="cb9-17"></span>
<span id="cb9-18"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Species in GBIF but not in field survey</span></span>
<span id="cb9-19">gbif_only <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">anti_join</span>(</span>
<span id="cb9-20">  gbif_area <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">distinct</span>(species),</span>
<span id="cb9-21">  species_data <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">distinct</span>(species),</span>
<span id="cb9-22">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">by =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"species"</span></span>
<span id="cb9-23">)</span></code></pre></div>
<p>The GBIF comparison belongs in the methods section, not the species inventory itself. Our earlier post on <a href="../../../blog/posts/gbif-kenya-data-quality/">GBIF Kenya data quality</a> covers the coordinate cleaning and taxonomic validation steps you should apply to this GBIF pull before using it as a cross-reference. Raw GBIF data without quality filtering will flag phantom species and undermine the comparison.</p>
</section>
<section id="project-structure-for-biodiversity-annexes" class="level2">
<h2 class="anchored" data-anchor-id="project-structure-for-biodiversity-annexes">Project Structure for Biodiversity Annexes</h2>
<p>A clean directory layout keeps the pipeline self-contained and legible to any colleague who picks it up:</p>
<pre><code>project-eia-001/
├── biodiversity_annex.qmd       # Parameterised template
├── R/
│   ├── fetch_iucn_status.R      # Cached IUCN lookup function
│   ├── compute_richness.R       # Richness and diversity indices
│   └── build_annex_tables.R     # kable table builders
├── data/
│   ├── raw/
│   │   └── biodiversity_field_records.csv   # Source of truth: never edit directly
│   ├── reference/
│   │   └── iucn_cache.rds       # Cached IUCN API responses
│   └── processed/               # Outputs written by pipeline
└── output/                      # Rendered PDFs</code></pre>
<p>The <code>data/raw/</code> CSV is the only file field staff need to update. The <code>R/</code> functions are testable in isolation. The <code>iucn_cache.rds</code> persists between renders. If you also run a <code>{targets}</code> pipeline for the full EIA (as described in our post on <a href="../../../blog/posts/esia-reproducibility/">EIA reproducibility</a>), the biodiversity tibble is one target in that pipeline, with the annex tables as downstream targets.</p>
</section>
<section id="the-commercial-case" class="level2">
<h2 class="anchored" data-anchor-id="the-commercial-case">The Commercial Case</h2>
<p>A single annex regeneration typically takes forty minutes of manual effort on a typical Kenyan EIA project: find the original data, update the relevant rows, reformat the table, check the column totals, update the summary statistics in the narrative, and check whether the threat-status flags are still correct. With the workflow above, the same update takes under two minutes.</p>
<p>Across four concurrent ASAL county screenings or three SGR-corridor EIAs running simultaneously, that difference compounds. The bigger cost, though, is the rejection risk. A NEMA review officer who finds a species count in Annex C that differs from Chapter 4 by even a small margin has grounds to return the report, and the revision clock restarts. The canonical tibble approach makes that class of error structurally impossible.</p>
<p>The data model, IUCN lookup, richness calculation, and parameterised output described here are the specific pieces that other posts in this series do not cover. The <a href="../../../blog/posts/gbif-kenya-data-quality/">GBIF Kenya data quality</a> post handles the upstream data sourcing. The broader pipeline and audit-trail approach are covered in <a href="../../../blog/posts/esia-reproducibility/">ESIA reproducibility</a>. This piece fills the gap: the biodiversity data model itself, and the code that turns it into compliant NEMA annex tables.</p>
<p>If your consultancy produces more than two EIAs per year and still assembles biodiversity annexes by hand, what is the cost of the next rejection?</p>
<hr>
<p><em>Kwiz Computing Technologies provides environmental data science services for EIA and ESIA projects across East Africa. Contact us to discuss reproducible biodiversity baseline workflows for your NEMA submissions.</em></p>


</section>

 ]]></description>
  <category>environmental-analytics</category>
  <category>EIA</category>
  <guid>https://kwizresearch.com/blog/posts/reproducible-eia-baseline-reports-r-nema-biodiversity-inventory-automation/</guid>
  <pubDate>Thu, 23 Apr 2026 00:00:00 GMT</pubDate>
  <media:content url="https://kwizresearch.com/blog/posts/reproducible-eia-baseline-reports-r-nema-biodiversity-inventory-automation/thumbnail.png" medium="image" type="image/png"/>
</item>
<item>
  <title>R Plumber API: Auth, Rate Limiting &amp; Deployment</title>
  <dc:creator>Kwiz Computing Technologies</dc:creator>
  <link>https://kwizresearch.com/blog/posts/production-rest-api-r-plumber-authentication-rate-limiting/</link>
  <description><![CDATA[ 




<p>Your Plumber API works on your laptop. Then a teammate hits the endpoint without a token, a rogue script fires 400 requests per minute, and the whole thing falls over at 2 a.m. This gap between tutorial code and production code is where most R developers lose time.</p>
<p>This article covers what you actually need: API key authentication, JWT verification, rate limiting middleware, a clean app structure, Docker packaging, and deployment on a VPS. All with working R code.</p>
<section id="why-hello-world-plumber-tutorials-leave-you-exposed" class="level2">
<h2 class="anchored" data-anchor-id="why-hello-world-plumber-tutorials-leave-you-exposed">Why Hello-World Plumber Tutorials Leave You Exposed</h2>
<p>The standard Plumber quickstart looks like this:</p>
<div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb1-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(plumber)</span>
<span id="cb1-2"></span>
<span id="cb1-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#* @get /predict</span></span>
<span id="cb1-4"><span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(x) {</span>
<span id="cb1-5">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">result =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.numeric</span>(x) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb1-6">}</span></code></pre></div>
<p>That is enough to demonstrate the concept. It is not enough for production. The endpoint accepts any request from anyone, has no error handling, logs nothing, and crashes if <code>x</code> is not numeric. On a shared server, one malformed request can take down every endpoint.</p>
<p>Production APIs need four things the tutorial skips: authentication, rate limiting, structured error handling, and a deployment target that stays running after you close your terminal.</p>
</section>
<section id="structuring-a-plumber-app-for-production" class="level2">
<h2 class="anchored" data-anchor-id="structuring-a-plumber-app-for-production">Structuring a Plumber App for Production</h2>
<p>Separate your concerns from the start. A flat <code>api.R</code> file gets unmanageable fast. Instead, use this layout:</p>
<pre><code>api/
├── plumber.R        # Router definition and filter registration
├── auth.R           # Authentication helpers
├── rate_limit.R     # Rate limiting state and logic
├── endpoints/
│   ├── predict.R
│   └── data.R
└── run.R            # Entry point</code></pre>
<p>Your <code>run.R</code> keeps the server start separate from the router logic:</p>
<div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb3-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(plumber)</span>
<span id="cb3-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">source</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"plumber.R"</span>)</span>
<span id="cb3-3"></span>
<span id="cb3-4"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">pr</span>() <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb3-5">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">pr_run</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">host =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"0.0.0.0"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">port =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">8000</span>)</span></code></pre></div>
<p>Your <code>plumber.R</code> defines the router and registers filters before any endpoint:</p>
<div class="sourceCode" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb4-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(plumber)</span>
<span id="cb4-2"></span>
<span id="cb4-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">source</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"auth.R"</span>)</span>
<span id="cb4-4"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">source</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"rate_limit.R"</span>)</span>
<span id="cb4-5"></span>
<span id="cb4-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#* @apiTitle Kwiz Analytics API</span></span>
<span id="cb4-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#* @apiVersion 1.0.0</span></span>
<span id="cb4-8"></span>
<span id="cb4-9"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Filters run in registration order before every matching endpoint</span></span>
<span id="cb4-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#* @filter log_request</span></span>
<span id="cb4-11">log_request <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(req, res) {</span>
<span id="cb4-12">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">cat</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">format</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">Sys.time</span>()), req<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>REQUEST_METHOD, req<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>PATH_INFO,</span>
<span id="cb4-13">      req<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>HTTP_X_FORWARDED_FOR <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%||%</span> req<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>REMOTE_ADDR, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">\n</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb4-14">  plumber<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">forward</span>()</span>
<span id="cb4-15">}</span>
<span id="cb4-16"></span>
<span id="cb4-17"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#* @filter authenticate</span></span>
<span id="cb4-18">authenticate_request <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(req, res) {</span>
<span id="cb4-19">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">authenticate</span>(req, res)</span>
<span id="cb4-20">  plumber<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">forward</span>()</span>
<span id="cb4-21">}</span>
<span id="cb4-22"></span>
<span id="cb4-23"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#* @filter rate_limit</span></span>
<span id="cb4-24">rate_limit_request <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(req, res) {</span>
<span id="cb4-25">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">check_rate_limit</span>(req, res)</span>
<span id="cb4-26">  plumber<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">forward</span>()</span>
<span id="cb4-27">}</span>
<span id="cb4-28"></span>
<span id="cb4-29"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Include endpoint files</span></span>
<span id="cb4-30"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#* @plumber</span></span>
<span id="cb4-31"><span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(pr) {</span>
<span id="cb4-32">  pr <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb4-33">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">pr_mount</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"/predict"</span>, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plumb</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"endpoints/predict.R"</span>)) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb4-34">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">pr_mount</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"/data"</span>,    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plumb</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"endpoints/data.R"</span>))</span>
<span id="cb4-35">}</span></code></pre></div>
<p>The <code>%||%</code> operator is a simple null-coalescing helper. Add it to <code>auth.R</code>:</p>
<div class="sourceCode" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb5-1"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">%||%</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span> <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(a, b) <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> (<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">is.null</span>(a) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&amp;&amp;</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">nchar</span>(a) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>) a <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">else</span> b</span></code></pre></div>
</section>
<section id="api-key-authentication-with-plumber-filters" class="level2">
<h2 class="anchored" data-anchor-id="api-key-authentication-with-plumber-filters">API Key Authentication with Plumber Filters</h2>
<p>Filters are the right place for authentication. They run before your endpoint function, so a rejected request never reaches your business logic.</p>
<p>A minimal API key system stores hashed keys in a flat file or database. This example uses a named list as an in-memory store. In a real deployment, read from a Postgres table or Redis on startup.</p>
<div class="sourceCode" id="cb6" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb6-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># auth.R</span></span>
<span id="cb6-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(openssl)</span>
<span id="cb6-3"></span>
<span id="cb6-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># In production, load from environment or database at startup</span></span>
<span id="cb6-5">VALID_KEYS <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(</span>
<span id="cb6-6">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"client_nairobi_erp"</span>  <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">=</span> digest<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">digest</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"sk_live_abc123"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">algo =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"sha256"</span>),</span>
<span id="cb6-7">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"client_mombasa_dash"</span> <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">=</span> digest<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">digest</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"sk_live_xyz789"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">algo =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"sha256"</span>)</span>
<span id="cb6-8">)</span>
<span id="cb6-9"></span>
<span id="cb6-10">authenticate <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(req, res) {</span>
<span id="cb6-11">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Accept key from header (preferred) or query string (for testing only)</span></span>
<span id="cb6-12">  api_key <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> req<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>HTTP_X_API_KEY <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%||%</span> req<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>args<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>api_key</span>
<span id="cb6-13"></span>
<span id="cb6-14">  <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> (<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">is.null</span>(api_key) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">||</span> api_key <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">""</span>) {</span>
<span id="cb6-15">    res<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>status <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">401</span></span>
<span id="cb6-16">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">stop</span>(jsonlite<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">toJSON</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">error =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Missing API key"</span>), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">auto_unbox =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>))</span>
<span id="cb6-17">  }</span>
<span id="cb6-18"></span>
<span id="cb6-19">  key_hash <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> digest<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">digest</span>(api_key, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">algo =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"sha256"</span>)</span>
<span id="cb6-20"></span>
<span id="cb6-21">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Constant-time comparison to prevent timing attacks</span></span>
<span id="cb6-22">  valid <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">any</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">vapply</span>(VALID_KEYS, <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(h) <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">identical</span>(h, key_hash), <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">logical</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)))</span>
<span id="cb6-23"></span>
<span id="cb6-24">  <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> (<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!</span>valid) {</span>
<span id="cb6-25">    res<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>status <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">403</span></span>
<span id="cb6-26">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">stop</span>(jsonlite<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">toJSON</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">error =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Invalid API key"</span>), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">auto_unbox =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>))</span>
<span id="cb6-27">  }</span>
<span id="cb6-28"></span>
<span id="cb6-29">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Attach client identity to request for downstream use</span></span>
<span id="cb6-30">  req<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>client_id <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">names</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">which</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">vapply</span>(VALID_KEYS,</span>
<span id="cb6-31">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(h) <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">identical</span>(h, key_hash), <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">logical</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>))))[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]</span>
<span id="cb6-32"></span>
<span id="cb6-33">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">invisible</span>(<span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">NULL</span>)</span>
<span id="cb6-34">}</span></code></pre></div>
<p>The <code>stop()</code> pattern halts filter execution and returns the error body to the caller. Plumber catches the condition and sends the response.</p>
</section>
<section id="jwt-authentication-for-user-scoped-endpoints" class="level2">
<h2 class="anchored" data-anchor-id="jwt-authentication-for-user-scoped-endpoints">JWT Authentication for User-Scoped Endpoints</h2>
<p>API keys work well for service-to-service calls. For endpoints that serve individual users (common when a Shiny app is the frontend), JWTs let you encode identity and expiry without a database lookup on every request.</p>
<div class="sourceCode" id="cb7" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb7-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># jwt_auth.R</span></span>
<span id="cb7-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(jose)</span>
<span id="cb7-3"></span>
<span id="cb7-4">JWT_SECRET <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">Sys.getenv</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"JWT_SECRET"</span>)  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Set in .Renviron or Docker env</span></span>
<span id="cb7-5"></span>
<span id="cb7-6">issue_token <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(user_id, role, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">expires_hours =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">8</span>) {</span>
<span id="cb7-7">  payload <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(</span>
<span id="cb7-8">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">sub  =</span> user_id,</span>
<span id="cb7-9">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">role =</span> role,</span>
<span id="cb7-10">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">iat  =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.integer</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">Sys.time</span>()),</span>
<span id="cb7-11">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">exp  =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.integer</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">Sys.time</span>()) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> (expires_hours <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3600</span>)</span>
<span id="cb7-12">  )</span>
<span id="cb7-13">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">jwt_encode_hmac</span>(payload, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">secret =</span> JWT_SECRET)</span>
<span id="cb7-14">}</span>
<span id="cb7-15"></span>
<span id="cb7-16">verify_token <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(req, res) {</span>
<span id="cb7-17">  auth_header <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> req<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>HTTP_AUTHORIZATION</span>
<span id="cb7-18">  <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> (<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">is.null</span>(auth_header) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">||</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">startsWith</span>(auth_header, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Bearer "</span>)) {</span>
<span id="cb7-19">    res<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>status <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">401</span></span>
<span id="cb7-20">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">stop</span>(jsonlite<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">toJSON</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">error =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Missing bearer token"</span>), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">auto_unbox =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>))</span>
<span id="cb7-21">  }</span>
<span id="cb7-22"></span>
<span id="cb7-23">  token <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sub</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"^Bearer "</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">""</span>, auth_header)</span>
<span id="cb7-24"></span>
<span id="cb7-25">  claims <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tryCatch</span>(</span>
<span id="cb7-26">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">jwt_decode_hmac</span>(token, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">secret =</span> JWT_SECRET),</span>
<span id="cb7-27">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">error =</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(e) <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">NULL</span></span>
<span id="cb7-28">  )</span>
<span id="cb7-29"></span>
<span id="cb7-30">  <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> (<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">is.null</span>(claims)) {</span>
<span id="cb7-31">    res<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>status <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">401</span></span>
<span id="cb7-32">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">stop</span>(jsonlite<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">toJSON</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">error =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Invalid or expired token"</span>), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">auto_unbox =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>))</span>
<span id="cb7-33">  }</span>
<span id="cb7-34"></span>
<span id="cb7-35">  <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> (<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.integer</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">Sys.time</span>()) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> claims<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>exp) {</span>
<span id="cb7-36">    res<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>status <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">401</span></span>
<span id="cb7-37">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">stop</span>(jsonlite<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">toJSON</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">error =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Token expired"</span>), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">auto_unbox =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>))</span>
<span id="cb7-38">  }</span>
<span id="cb7-39"></span>
<span id="cb7-40">  req<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>user_id <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> claims<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>sub</span>
<span id="cb7-41">  req<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>user_role <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> claims<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>role</span>
<span id="cb7-42">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">invisible</span>(<span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">NULL</span>)</span>
<span id="cb7-43">}</span></code></pre></div>
<p>Your Shiny frontend calls <code>/auth/login</code> to get a token, then includes it in every subsequent API request as <code>Authorization: Bearer &lt;token&gt;</code>. This pairs naturally with the architecture described in the <a href="../../../blog/posts/rshiny-hosting-guide/">R Shiny hosting guide</a>.</p>
</section>
<section id="rate-limiting-with-a-token-bucket" class="level2">
<h2 class="anchored" data-anchor-id="rate-limiting-with-a-token-bucket">Rate Limiting with a Token Bucket</h2>
<p>Rate limiting protects your API from both malicious abuse and accidental loops in client code. The token bucket algorithm is simple to implement in R: each client gets a bucket of N tokens that refills at a fixed rate. Each request consumes one token. When the bucket is empty, the request is rejected.</p>
<div class="sourceCode" id="cb8" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb8-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># rate_limit.R</span></span>
<span id="cb8-2"></span>
<span id="cb8-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># In-memory state: named list of lists(tokens, last_refill)</span></span>
<span id="cb8-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># For multi-process deployments, move this to Redis via the redux package</span></span>
<span id="cb8-5">rate_limit_state <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">new.env</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">hash =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">parent =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">emptyenv</span>())</span>
<span id="cb8-6"></span>
<span id="cb8-7">BUCKET_CAPACITY  <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">60</span>   <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Max tokens per client</span></span>
<span id="cb8-8">REFILL_RATE      <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">60</span>   <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Tokens added per minute</span></span>
<span id="cb8-9">REFILL_INTERVAL  <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">60</span>   <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Seconds between refills</span></span>
<span id="cb8-10"></span>
<span id="cb8-11">get_client_key <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(req) {</span>
<span id="cb8-12">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Use API key identity if available, otherwise IP</span></span>
<span id="cb8-13">  req<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>client_id <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%||%</span> (req<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>HTTP_X_FORWARDED_FOR <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%||%</span> req<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>REMOTE_ADDR)</span>
<span id="cb8-14">}</span>
<span id="cb8-15"></span>
<span id="cb8-16">check_rate_limit <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(req, res) {</span>
<span id="cb8-17">  client <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">get_client_key</span>(req)</span>
<span id="cb8-18">  now    <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.numeric</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">Sys.time</span>())</span>
<span id="cb8-19"></span>
<span id="cb8-20">  <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> (<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">exists</span>(client, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">envir =</span> rate_limit_state)) {</span>
<span id="cb8-21">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">assign</span>(client,</span>
<span id="cb8-22">           <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">tokens =</span> BUCKET_CAPACITY, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">last_refill =</span> now),</span>
<span id="cb8-23">           <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">envir =</span> rate_limit_state)</span>
<span id="cb8-24">  }</span>
<span id="cb8-25"></span>
<span id="cb8-26">  state <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">get</span>(client, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">envir =</span> rate_limit_state)</span>
<span id="cb8-27"></span>
<span id="cb8-28">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Refill tokens based on elapsed time</span></span>
<span id="cb8-29">  elapsed       <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> now <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> state<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>last_refill</span>
<span id="cb8-30">  refill_amount <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">floor</span>(elapsed <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> REFILL_INTERVAL) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> REFILL_RATE</span>
<span id="cb8-31">  new_tokens    <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">min</span>(BUCKET_CAPACITY, state<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>tokens <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> refill_amount)</span>
<span id="cb8-32">  last_refill   <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> (refill_amount <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>) now <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">else</span> state<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>last_refill</span>
<span id="cb8-33"></span>
<span id="cb8-34">  <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> (new_tokens <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>) {</span>
<span id="cb8-35">    res<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>status <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">429</span></span>
<span id="cb8-36">    res<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">setHeader</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Retry-After"</span>, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ceiling</span>(REFILL_INTERVAL <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> (now <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> last_refill)))</span>
<span id="cb8-37">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">stop</span>(jsonlite<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">toJSON</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">error =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Rate limit exceeded"</span>), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">auto_unbox =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>))</span>
<span id="cb8-38">  }</span>
<span id="cb8-39"></span>
<span id="cb8-40">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Consume one token</span></span>
<span id="cb8-41">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">assign</span>(client,</span>
<span id="cb8-42">         <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">tokens =</span> new_tokens <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">last_refill =</span> last_refill),</span>
<span id="cb8-43">         <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">envir =</span> rate_limit_state)</span>
<span id="cb8-44"></span>
<span id="cb8-45">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">invisible</span>(<span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">NULL</span>)</span>
<span id="cb8-46">}</span></code></pre></div>
<p>This in-memory approach works for a single-process deployment. For multiple workers, replace <code>rate_limit_state</code> with Redis calls via the <code>redux</code> package: each check becomes <code>HINCRBY</code> and <code>EXPIRE</code> operations on a Redis hash keyed by client ID.</p>
</section>
<section id="packaging-with-docker" class="level2">
<h2 class="anchored" data-anchor-id="packaging-with-docker">Packaging with Docker</h2>
<p>Docker solves the “works on my machine” problem. It also makes deployment to any Linux VPS straightforward, including the affordable DigitalOcean droplets and Hetzner servers that many East African development teams use when AWS credit access is limited.</p>
<p>Create a <code>Dockerfile</code> at the project root:</p>
<div class="sourceCode" id="cb9" style="background: #f1f3f5;"><pre class="sourceCode dockerfile code-with-copy"><code class="sourceCode dockerfile"><span id="cb9-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">FROM</span> rocker/r-ver:4.4.0</span>
<span id="cb9-2"></span>
<span id="cb9-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># System dependencies</span></span>
<span id="cb9-4"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">RUN</span> <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">apt-get</span> update <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">&amp;&amp;</span> <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">apt-get</span> install <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-y</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">\</span></span>
<span id="cb9-5">    libcurl4-openssl-dev <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">\</span></span>
<span id="cb9-6">    libssl-dev <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">\</span></span>
<span id="cb9-7">    libsodium-dev <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">\</span></span>
<span id="cb9-8">    <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">&amp;&amp;</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">rm</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-rf</span> /var/lib/apt/lists/<span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">*</span></span>
<span id="cb9-9"></span>
<span id="cb9-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Install R packages</span></span>
<span id="cb9-11"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">RUN</span> <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">Rscript</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-e</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"install.packages(c(</span></span>
<span id="cb9-12"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  'plumber', 'jsonlite', 'digest', 'jose', 'redux', 'logger'</span></span>
<span id="cb9-13"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">), repos = 'https://cloud.r-project.org')"</span></span>
<span id="cb9-14"></span>
<span id="cb9-15"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">WORKDIR</span> /api</span>
<span id="cb9-16"></span>
<span id="cb9-17"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Copy application code</span></span>
<span id="cb9-18"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">COPY</span> api/ .</span>
<span id="cb9-19"></span>
<span id="cb9-20"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">EXPOSE</span> 8000</span>
<span id="cb9-21"></span>
<span id="cb9-22"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">CMD</span> [<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Rscript"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"run.R"</span>]</span></code></pre></div>
<p>Build and run locally to verify:</p>
<div class="sourceCode" id="cb10" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb10-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">docker</span> build <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-t</span> kwiz-api:latest .</span>
<span id="cb10-2"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">docker</span> run <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-p</span> 8000:8000 <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">\</span></span>
<span id="cb10-3">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-e</span> JWT_SECRET=<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"your-secret-here"</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">\</span></span>
<span id="cb10-4">  kwiz-api:latest</span></code></pre></div>
<p>Test authentication before shipping:</p>
<div class="sourceCode" id="cb11" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb11-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Should return 401</span></span>
<span id="cb11-2"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">curl</span> http://localhost:8000/predict/score</span>
<span id="cb11-3"></span>
<span id="cb11-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Should return data</span></span>
<span id="cb11-5"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">curl</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-H</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"X-Api-Key: sk_live_abc123"</span> http://localhost:8000/predict/score<span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">?</span>x=42</span></code></pre></div>
</section>
<section id="deploying-to-a-vps" class="level2">
<h2 class="anchored" data-anchor-id="deploying-to-a-vps">Deploying to a VPS</h2>
<p>A 2 GB RAM DigitalOcean droplet (about $12/month, payable in KES via M-Pesa through Safaricom or PayPal) handles moderate API traffic without the complexity of managed Kubernetes. The deployment process is the same on Hetzner, Vultr, or any other provider.</p>
<p>On the server, install Docker and <code>docker-compose</code>, then write a <code>docker-compose.yml</code>:</p>
<div class="sourceCode" id="cb12" style="background: #f1f3f5;"><pre class="sourceCode yaml code-with-copy"><code class="sourceCode yaml"><span id="cb12-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">version</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"3.8"</span></span>
<span id="cb12-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">services</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb12-3"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">api</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb12-4"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">image</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> kwiz-api:latest</span></span>
<span id="cb12-5"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">restart</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> always</span></span>
<span id="cb12-6"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ports</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb12-7"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"8000:8000"</span></span>
<span id="cb12-8"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">environment</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb12-9"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> JWT_SECRET=${JWT_SECRET}</span></span>
<span id="cb12-10"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">volumes</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb12-11"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> ./logs:/api/logs</span></span>
<span id="cb12-12"></span>
<span id="cb12-13"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">nginx</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb12-14"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">image</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> nginx:alpine</span></span>
<span id="cb12-15"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">restart</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> always</span></span>
<span id="cb12-16"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ports</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb12-17"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"80:80"</span></span>
<span id="cb12-18"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"443:443"</span></span>
<span id="cb12-19"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">volumes</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb12-20"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> ./nginx.conf:/etc/nginx/conf.d/default.conf</span></span>
<span id="cb12-21"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> /etc/letsencrypt:/etc/letsencrypt:ro</span></span></code></pre></div>
<p>Your <code>nginx.conf</code> handles TLS termination and proxies requests to the Plumber container. Use <code>certbot</code> with Let’s Encrypt for free TLS certificates. This matches the <a href="../../../blog/posts/quarto-cloud-deployment/">Quarto cloud deployment pattern</a> if you are hosting both your site and API on the same server.</p>
<p>Push your image to Docker Hub or GitHub Container Registry, then pull and start on the server:</p>
<div class="sourceCode" id="cb13" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb13-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">docker-compose</span> pull</span>
<span id="cb13-2"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">docker-compose</span> up <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-d</span></span></code></pre></div>
<p>Set <code>restart: always</code> so the container comes back up after a server reboot or crash.</p>
</section>
<section id="connecting-a-shiny-frontend" class="level2">
<h2 class="anchored" data-anchor-id="connecting-a-shiny-frontend">Connecting a Shiny Frontend</h2>
<p>Once the API is running, your Shiny app calls it with <code>httr2</code>:</p>
<div class="sourceCode" id="cb14" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb14-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(httr2)</span>
<span id="cb14-2"></span>
<span id="cb14-3">get_prediction <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(x, api_key) {</span>
<span id="cb14-4">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">request</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"https://api.yourdomain.com/predict/score"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb14-5">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">req_headers</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"X-Api-Key"</span> <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">=</span> api_key) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb14-6">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">req_url_query</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">x =</span> x) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb14-7">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">req_perform</span>() <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb14-8">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">resp_body_json</span>()</span>
<span id="cb14-9">}</span></code></pre></div>
<p>This separation keeps your Shiny app stateless. The API handles computation and data access; Shiny handles the interface. Teams can update the model backend without redeploying the frontend, and multiple Shiny apps can share the same API. For teams already following this approach, see how it fits into a broader enterprise R architecture in our <a href="../../../blog/posts/r-for-business-leaders/">guide for business leaders on R</a>.</p>
<p>Several enterprise clients we work with in Nairobi run this exact stack: a Plumber API on a VPS handling model inference, Shiny apps connecting to it from shinyapps.io, and API keys rotated quarterly. It keeps infrastructure costs low while maintaining clear security boundaries.</p>
</section>
<section id="what-to-build-next" class="level2">
<h2 class="anchored" data-anchor-id="what-to-build-next">What to Build Next</h2>
<p>The patterns here scale further. Add an <code>/auth/token</code> endpoint that issues short-lived JWTs in exchange for valid API keys. Wire the rate limiter to Redis so it works across multiple API containers behind a load balancer. Add a <code>/health</code> endpoint that checks database connectivity and returns a non-200 status if anything is broken, so your uptime monitor catches problems before users do.</p>
<p>If your team is exposing model predictions or data pipelines as HTTP endpoints and you want a review of the architecture or help with the deployment, <a href="../../..">Kwiz Computing</a> works with data science teams across East Africa on exactly this kind of infrastructure.</p>
<p>What part of the Plumber-to-production gap is slowing your team down most? Authentication, deployment, or scaling?</p>


</section>

 ]]></description>
  <category>enterprise-data-science</category>
  <category>R-Plumber</category>
  <guid>https://kwizresearch.com/blog/posts/production-rest-api-r-plumber-authentication-rate-limiting/</guid>
  <pubDate>Thu, 23 Apr 2026 00:00:00 GMT</pubDate>
  <media:content url="https://kwizresearch.com/blog/posts/production-rest-api-r-plumber-authentication-rate-limiting/thumbnail.png" medium="image" type="image/png"/>
</item>
<item>
  <title>Carbon Credit Pricing in Kenya: A Quantitative Approach</title>
  <dc:creator>Kwiz Computing Technologies</dc:creator>
  <link>https://kwizresearch.com/blog/posts/quantitative-carbon-credit-pricing-kenya-project-developers/</link>
  <description><![CDATA[ 




<section id="the-spreadsheet-problem" class="level2">
<h2 class="anchored" data-anchor-id="the-spreadsheet-problem">The Spreadsheet Problem</h2>
<p>Most Kenyan carbon project developers price their credits the same way: find a few comparable transactions, average them, and write that number into a project information document. That method worked when buyers had no analytical tools and no public benchmarks. Both conditions no longer apply.</p>
<p>Institutional carbon buyers in 2026 run their own hedonic models before entering any negotiation. If your pricing rationale is a single number without an uncertainty range, you are negotiating without preparation against a counterpart who has done the analysis. The gap between a well-modelled credit and a spreadsheet estimate is commonly 20-40% of final transaction price.</p>
</section>
<section id="what-kariba-taught-the-market" class="level2">
<h2 class="anchored" data-anchor-id="what-kariba-taught-the-market">What Kariba Taught the Market</h2>
<p>The Kariba REDD+ project in Zimbabwe became the most discussed over-crediting case in voluntary carbon market history. Independent analysis published by Carbon Market Watch and others showed that the baseline deforestation rate used to calculate avoided emissions was set far above what satellite data and matching-region comparisons could justify, resulting in the issuance of millions of credits that did not represent real emission reductions.</p>
<p>Kariba directly affected Kenyan projects because scrutiny radiates. After the investigation, buyers began applying much tighter due diligence to all African REDD+ credits, including projects under the Northern Rangelands Trust (NRT), which operates across arid and semi-arid lands in Samburu, Isiolo, and Laikipia counties. The NRT credits are methodologically distinct from Kariba and generally regarded as higher-quality, but the market re-pricing that followed Kariba’s exposure affected the entire regional asset class.</p>
<p>The lesson for Kenyan developers is not just about methodology integrity. It is about pricing defensibility: can you show a buyer exactly where your credit sits in the distribution of comparable assets, with quantified uncertainty, before they ask?</p>
</section>
<section id="what-institutional-buyers-now-require" class="level2">
<h2 class="anchored" data-anchor-id="what-institutional-buyers-now-require">What Institutional Buyers Now Require</h2>
<p>The voluntary carbon market has moved from qualitative ratings to quantitative scoring. BeZero Carbon and Sylvera both publish probability-of-credit-performance ratings derived from satellite monitoring, additionality analysis, and permanence risk models. Buyers use these ratings as a direct input into pricing.</p>
<p>The Kenya Carbon Market Authority (CMA) Bill, currently advancing through Kenya’s legislative pipeline, will formalise domestic verification and registry requirements. Once enacted, it will require projects to demonstrate pricing based on validated methodologies, not informal benchmarks. Projects that cannot produce defensible price estimates with documented uncertainty ranges will face longer approval timelines and reduced buyer confidence.</p>
<p>The market has also fragmented by quality tier. A Verra-registered cookstoves credit with a BeZero A rating does not price the same as an unrated REDD+ credit from the same country. The spread between the top and bottom quality tiers in the voluntary carbon market widened to roughly USD 30 per tonne by 2024 and has not compressed. Models that ignore this spread undervalue the best projects and overprice the weakest.</p>
</section>
<section id="the-quantitative-approach" class="level2">
<h2 class="anchored" data-anchor-id="the-quantitative-approach">The Quantitative Approach</h2>
<p>Voluntary carbon credit prices are not random. They vary systematically with a small set of observable characteristics. The quantitative approach is a hedonic pricing model: regress observed transaction prices against project characteristics to estimate the contribution of each characteristic to price, then use those coefficients to price a new project.</p>
<p>The key co-variates are:</p>
<ul>
<li><strong>Project type:</strong> REDD+ commands a discount versus improved cookstoves and blue carbon in most recent samples. Cookstoves projects under the Gold Standard have benefited from stronger co-benefit narratives (health, gender, clean air). Blue carbon is a premium niche with limited supply from East Africa.</li>
<li><strong>Registry:</strong> Verra (VCS) is the volume registry. Gold Standard carries a consistent premium, historically 20-40% above comparable VCS credits.</li>
<li><strong>Vintage year:</strong> Older vintages trade at a discount as buyers become more aware of permanence risks and baseline drift. Expect roughly USD 0.50-1.50 per tonne discount per year of vintage age in the current market.</li>
<li><strong>Rating:</strong> BeZero A-rated credits price significantly above unrated equivalents. Sylvera’s top tier shows similar effects. The rating premium is the strongest single predictor in recent transaction data.</li>
<li><strong>Country and region:</strong> African credits as a class traded at a discount relative to Latin American REDD+ during the 2023-2024 scrutiny period. That discount is narrowing for well-rated East African projects.</li>
</ul>
</section>
<section id="a-minimal-r-implementation" class="level2">
<h2 class="anchored" data-anchor-id="a-minimal-r-implementation">A Minimal R Implementation</h2>
<p>The code below builds a linear pricing model from a representative sample of voluntary carbon market transactions and uses it to produce a prediction interval for a new Kenyan project. In practice, you would source transaction data from platforms like xpansiv/CBL, MSCI’s Carbon Markets data products, or aggregated registry issuance and cancellation records.</p>
<div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb1-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(tidyverse)</span>
<span id="cb1-2"></span>
<span id="cb1-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Simulated VCM transaction data with realistic structure</span></span>
<span id="cb1-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># In production: replace with CBL/xpansiv API data or</span></span>
<span id="cb1-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># manually cleaned registry transaction records</span></span>
<span id="cb1-6"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">set.seed</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">42</span>)</span>
<span id="cb1-7">n <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">180</span></span>
<span id="cb1-8"></span>
<span id="cb1-9">vcm_transactions <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tibble</span>(</span>
<span id="cb1-10">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">price_usd_tco2e =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">NA_real_</span>,</span>
<span id="cb1-11">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">project_type    =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sample</span>(</span>
<span id="cb1-12">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"REDD+"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"cookstoves"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"blue_carbon"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"afforestation"</span>),</span>
<span id="cb1-13">    n, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">replace =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">prob =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.45</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.30</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.10</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.15</span>)</span>
<span id="cb1-14">  ),</span>
<span id="cb1-15">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">registry        =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sample</span>(</span>
<span id="cb1-16">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Verra"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Gold_Standard"</span>),</span>
<span id="cb1-17">    n, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">replace =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">prob =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.70</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.30</span>)</span>
<span id="cb1-18">  ),</span>
<span id="cb1-19">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">vintage_year    =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sample</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2018</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2023</span>, n, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">replace =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>),</span>
<span id="cb1-20">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">bezero_rating   =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sample</span>(</span>
<span id="cb1-21">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"A"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"BB"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"B"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"unrated"</span>),</span>
<span id="cb1-22">    n, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">replace =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">prob =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.15</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.25</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.30</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.30</span>)</span>
<span id="cb1-23">  ),</span>
<span id="cb1-24">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">african_project =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sample</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>, <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">FALSE</span>), n, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">replace =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">prob =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.40</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.60</span>))</span>
<span id="cb1-25">)</span>
<span id="cb1-26"></span>
<span id="cb1-27"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Simulate price as a function of known drivers</span></span>
<span id="cb1-28"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Coefficients approximate published hedonic estimates (Ecosystem Marketplace 2024)</span></span>
<span id="cb1-29">vcm_transactions <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> vcm_transactions <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb1-30">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mutate</span>(</span>
<span id="cb1-31">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">base_price =</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">8.0</span>,</span>
<span id="cb1-32">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type_adj   =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">case_when</span>(</span>
<span id="cb1-33">      project_type <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"cookstoves"</span>    <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">~</span>  <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">4.5</span>,</span>
<span id="cb1-34">      project_type <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"blue_carbon"</span>   <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">~</span>  <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">6.0</span>,</span>
<span id="cb1-35">      project_type <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"afforestation"</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">~</span>  <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.5</span>,</span>
<span id="cb1-36">      project_type <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"REDD+"</span>         <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">~</span>  <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.0</span></span>
<span id="cb1-37">    ),</span>
<span id="cb1-38">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">registry_adj  =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">if_else</span>(registry <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Gold_Standard"</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">3.5</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.0</span>),</span>
<span id="cb1-39">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">vintage_adj   =</span> (vintage_year <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2023</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.80</span>,</span>
<span id="cb1-40">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">rating_adj    =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">case_when</span>(</span>
<span id="cb1-41">      bezero_rating <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"A"</span>       <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">~</span>  <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">5.0</span>,</span>
<span id="cb1-42">      bezero_rating <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"BB"</span>      <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">~</span>  <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">2.5</span>,</span>
<span id="cb1-43">      bezero_rating <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"B"</span>       <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">~</span>  <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.5</span>,</span>
<span id="cb1-44">      bezero_rating <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"unrated"</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">~</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.5</span></span>
<span id="cb1-45">    ),</span>
<span id="cb1-46">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">africa_adj    =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">if_else</span>(african_project, <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.2</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.0</span>),</span>
<span id="cb1-47">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">noise         =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">rnorm</span>(n, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">2.5</span>),</span>
<span id="cb1-48">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">price_usd_tco2e =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">pmax</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, base_price <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> type_adj <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> registry_adj <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb1-49">                               vintage_adj <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> rating_adj <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> africa_adj <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> noise)</span>
<span id="cb1-50">  ) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb1-51">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">select</span>(<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>base_price, <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>type_adj, <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>registry_adj,</span>
<span id="cb1-52">         <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>vintage_adj, <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>rating_adj, <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>africa_adj, <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>noise)</span>
<span id="cb1-53"></span>
<span id="cb1-54"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Encode categorical predictors</span></span>
<span id="cb1-55">vcm_model_data <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> vcm_transactions <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb1-56">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mutate</span>(</span>
<span id="cb1-57">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">across</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(project_type, registry, bezero_rating), as.factor),</span>
<span id="cb1-58">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">vintage_age =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2024</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> vintage_year   <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># age in years relative to current</span></span>
<span id="cb1-59">  )</span>
<span id="cb1-60"></span>
<span id="cb1-61"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Fit hedonic pricing model</span></span>
<span id="cb1-62">price_model <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">lm</span>(</span>
<span id="cb1-63">  price_usd_tco2e <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">~</span> project_type <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> registry <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> vintage_age <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb1-64">                    bezero_rating <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> african_project,</span>
<span id="cb1-65">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">data =</span> vcm_model_data</span>
<span id="cb1-66">)</span>
<span id="cb1-67"></span>
<span id="cb1-68"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">summary</span>(price_model)</span></code></pre></div>
<p>The model summary shows coefficient estimates with standard errors. Each coefficient tells you the marginal price contribution of that characteristic, holding all others constant. A Gold Standard cookstoves credit from 2023 with a BeZero A rating is not just “a good credit”; it is a credit where you can quantify exactly how much each attribute contributes to its price.</p>
<div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb2-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Predict price for a new Northern Rangelands Trust REDD+ project</span></span>
<span id="cb2-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Assumptions: Verra registry, 2024 vintage, BeZero BB rating,</span></span>
<span id="cb2-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># African project (Kenya)</span></span>
<span id="cb2-4">new_project <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tibble</span>(</span>
<span id="cb2-5">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">project_type    =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">factor</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"REDD+"</span>,      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">levels =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">levels</span>(vcm_model_data<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>project_type)),</span>
<span id="cb2-6">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">registry        =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">factor</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Verra"</span>,      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">levels =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">levels</span>(vcm_model_data<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>registry)),</span>
<span id="cb2-7">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">vintage_age     =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>,                   <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 2024 vintage</span></span>
<span id="cb2-8">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">bezero_rating   =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">factor</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"BB"</span>,         <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">levels =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">levels</span>(vcm_model_data<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>bezero_rating)),</span>
<span id="cb2-9">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">african_project =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span></span>
<span id="cb2-10">)</span>
<span id="cb2-11"></span>
<span id="cb2-12"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Point estimate with 95% prediction interval</span></span>
<span id="cb2-13">prediction <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">predict</span>(</span>
<span id="cb2-14">  price_model,</span>
<span id="cb2-15">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">newdata   =</span> new_project,</span>
<span id="cb2-16">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">interval  =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"prediction"</span>,</span>
<span id="cb2-17">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">level     =</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.95</span></span>
<span id="cb2-18">)</span>
<span id="cb2-19"></span>
<span id="cb2-20"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">cat</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sprintf</span>(</span>
<span id="cb2-21">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Estimated price: USD %.2f/tCO2e</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">\n</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">95%% prediction interval: USD %.2f - USD %.2f</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">\n</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>,</span>
<span id="cb2-22">  prediction[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"fit"</span>],</span>
<span id="cb2-23">  prediction[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"lwr"</span>],</span>
<span id="cb2-24">  prediction[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"upr"</span>]</span>
<span id="cb2-25">))</span></code></pre></div>
<p>The prediction interval is the output that institutional buyers and the CMA will want to see. A point estimate of USD 9.50 per tonne is a claim. A point estimate of USD 9.50 with a 95% interval of USD 5.20 to USD 13.80 is a transparent disclosure of uncertainty that buyers can incorporate into their own valuation models.</p>
</section>
<section id="practical-application-for-kenyan-developers" class="level2">
<h2 class="anchored" data-anchor-id="practical-application-for-kenyan-developers">Practical Application for Kenyan Developers</h2>
<p>The Northern Rangelands Trust operates grassland carbon projects across roughly 42,000 square kilometres of community conservancies in northern Kenya. NRT credits are REDD+ by project type (protecting grassland and rangeland carbon stocks from degradation), registered under Verra VCS, and increasingly rated by third-party raters. They sit at a methodologically stronger position than many African REDD+ projects because NRT’s monitoring framework integrates ground-level pastoral management data with satellite biomass estimates.</p>
<p>To apply this model to an NRT-equivalent project or to any new Kenyan carbon development, you need four data points:</p>
<ol type="1">
<li><strong>Project type:</strong> REDD+, improved cookstoves, small-scale solar, blue carbon, or afforestation. This is determined by your methodology choice.</li>
<li><strong>Registry:</strong> Verra VCS or Gold Standard are the two registries that attract institutional buyer interest. Other registries carry a significant liquidity discount.</li>
<li><strong>Vintage year:</strong> The year credits are issued, which follows verification, which follows the monitoring period. Plan for a 12-18 month lag from project activity to issuance.</li>
<li><strong>Third-party rating:</strong> BeZero and Sylvera both accept project submissions. The rating process takes 3-6 months and costs on the order of USD 20,000-50,000 for a first assessment. For a project expecting to issue 100,000 tonnes per year, the pricing uplift from a strong rating typically covers that cost within the first issuance cycle.</li>
</ol>
<p>Running the model before you approach buyers tells you three things: where your credits sit relative to the market distribution, what the model-implied fair price range is, and which attribute changes (getting rated, switching registry, accelerating issuance to preserve vintage recency) would shift your price the most.</p>
<p>For a project issuing 500,000 tonnes over a 10-year crediting period, a 15% pricing improvement from a well-executed rating process represents USD 750,000 at USD 10 per tonne baseline. The analysis cost is a rounding error relative to that.</p>
</section>
<section id="defensible-pricing-is-not-optional-anymore" class="level2">
<h2 class="anchored" data-anchor-id="defensible-pricing-is-not-optional-anymore">Defensible Pricing Is Not Optional Anymore</h2>
<p>The voluntary carbon market in East Africa is entering a period where reputational and regulatory scrutiny will only increase. The Kenya CMA Bill, institutional buyer requirements for uncertainty quantification, and the fallout from high-profile over-crediting cases have together raised the bar for what a credible project pricing document looks like.</p>
<p>Systematic, data-driven pricing protects Kenyan developers on two fronts: it prevents underselling relative to project quality, and it provides documented methodology to show buyers, regulators, and the public when the inevitable questions arise.</p>
<p>The R approach shown here is a starting point. Production implementations benefit from richer transaction datasets, time-varying coefficients, and project-specific additionality risk priors. But even the minimal model outperforms a spreadsheet average in transparency, reproducibility, and defensibility.</p>
<p>For developers building this capability for the first time, our posts on <a href="../../../blog/posts/carbon-markets-quant-methods/">carbon markets and quantitative methods</a> and <a href="../../../blog/posts/kenya-open-data/">Kenya open data sources</a> cover the data infrastructure you need. The <a href="../../../blog/posts/esia-reproducibility/">reproducibility principles from EIA workflows</a> also apply directly: version-controlled code, documented assumptions, and rendered outputs that show methodology and results together.</p>
<p>What does your current pricing process look like? If you are working from comparable-transaction averages without uncertainty ranges, the model above is a two-hour implementation that immediately changes your negotiating position.</p>
<p>Kwiz Computing Technologies provides quantitative environmental analytics services for Kenyan and East African carbon project developers, including hedonic pricing models, additionality analysis, and monitoring data workflows. <a href="../../../contact.html">Reach out</a> if you want to build defensible pricing into your next project cycle.</p>


</section>

 ]]></description>
  <category>environmental-analytics</category>
  <category>carbon-markets</category>
  <guid>https://kwizresearch.com/blog/posts/quantitative-carbon-credit-pricing-kenya-project-developers/</guid>
  <pubDate>Thu, 23 Apr 2026 00:00:00 GMT</pubDate>
  <media:content url="https://kwizresearch.com/blog/posts/quantitative-carbon-credit-pricing-kenya-project-developers/thumbnail.png" medium="image" type="image/png"/>
</item>
<item>
  <title>Kenya NDC: Tracking Climate Commitments with Open Data</title>
  <dc:creator>Kwiz Computing Technologies</dc:creator>
  <link>https://kwizresearch.com/blog/posts/kenya-ndc-climate-commitments-tracking-open-data/</link>
  <description><![CDATA[ 




<section id="the-gap-between-filed-targets-and-measured-progress" class="level2">
<h2 class="anchored" data-anchor-id="the-gap-between-filed-targets-and-measured-progress">The Gap Between Filed Targets and Measured Progress</h2>
<p>Kenya submitted a Nationally Determined Contribution to the UNFCCC that commits to a 32% reduction in greenhouse gas emissions by 2030, conditional on international support. The document is public. The underlying emissions data is public too. Yet almost no one has put them together in a reproducible, updatable analysis.</p>
<p>That gap matters. NDCs are the core accountability mechanism of the Paris Agreement. Without systematic progress tracking, “conditional on international support” becomes an untestable claim, and the targets themselves lose credibility as planning instruments.</p>
</section>
<section id="what-kenyas-ndc-actually-commits-to" class="level2">
<h2 class="anchored" data-anchor-id="what-kenyas-ndc-actually-commits-to">What Kenya’s NDC Actually Commits To</h2>
<p>Kenya’s updated 2030 NDC, submitted in 2020, uses a 2016 baseline of approximately 73 MtCO2e. The 32% conditional target implies keeping total emissions at or below roughly 50 MtCO2e by 2030. A 3% unconditional reduction is committed regardless of external financing.</p>
<p>The sectoral breakdown covers four main areas:</p>
<ul>
<li><strong>Energy</strong>: efficiency improvements and renewable expansion, including geothermal and solar</li>
<li><strong>Transport</strong>: bus rapid transit, modal shift, and electric mobility targets in urban corridors</li>
<li><strong>Forestry and land use</strong>: increasing forest cover from roughly 7% to 10% of land area, protecting existing forests</li>
<li><strong>Agriculture</strong>: reduced emissions intensity from livestock, sustainable soil management</li>
</ul>
<p>Each sector has quantitative sub-targets in the NDC text, but the sectoral monitoring frameworks are not yet consistently populated. The national reporting to the UNFCCC, through Biennial Transparency Reports and National Communications, lags reality by three to five years in most cases.</p>
</section>
<section id="why-ndc-progress-tracking-stays-opaque" class="level2">
<h2 class="anchored" data-anchor-id="why-ndc-progress-tracking-stays-opaque">Why NDC Progress Tracking Stays Opaque</h2>
<p>The data problem is structural. Kenya’s emissions data is scattered across at least four distinct systems that do not talk to each other.</p>
<p>NEMA’s national GHG inventory follows IPCC methodology but is updated infrequently, with the most recent National Communication covering data through 2015. The Kenya Climate Action Tracker (part of the global CAT project) provides independent estimates but relies on its own modelling assumptions. EDGAR (Emissions Database for Global Atmospheric Research, produced by the EU Joint Research Centre) publishes annual country-level estimates using a consistent global methodology. PRIMAP-hist, maintained by the Potsdam Institute for Climate Impact Research, provides a third independent time series back to 1750.</p>
<p>None of these is “the” authoritative source. NEMA’s inventory is most Kenya-specific but least current. EDGAR and PRIMAP-hist are current but use global proxy methods where country-specific data is sparse. The Kenya National Bureau of Statistics publishes activity data (energy consumption, livestock numbers, land cover change) that feeds into these models but is rarely read alongside them.</p>
<p>For a policy professional trying to answer “is Kenya on track?”, choosing which dataset to use is already a methodological decision that most NDC progress reports leave implicit.</p>
</section>
<section id="accessing-edgar-and-primap-hist-in-r" class="level2">
<h2 class="anchored" data-anchor-id="accessing-edgar-and-primap-hist-in-r">Accessing EDGAR and PRIMAP-hist in R</h2>
<p>Both EDGAR and PRIMAP-hist publish data as downloadable CSV files with stable URLs. Neither requires an API key for basic access.</p>
<section id="edgar" class="level3">
<h3 class="anchored" data-anchor-id="edgar">EDGAR</h3>
<p>EDGAR v8.0, the current release, publishes total GHG data by country, year, and sector. The country-level file is the practical starting point.</p>
<div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb1-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(tidyverse)</span>
<span id="cb1-2"></span>
<span id="cb1-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># EDGAR v8.0 total GHG (CO2, CH4, N2O, F-gases) by country</span></span>
<span id="cb1-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Source: https://edgar.jrc.ec.europa.eu/dataset_ghg80</span></span>
<span id="cb1-5">edgar_url <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">paste0</span>(</span>
<span id="cb1-6">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"https://jeodpp.jrc.ec.europa.eu/ftp/jrc-opendata/EDGAR/datasets/"</span>,</span>
<span id="cb1-7">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"v80_FT2022_GHG/IEA_EDGAR_CO2_1970_2022.zip"</span></span>
<span id="cb1-8">)</span>
<span id="cb1-9"></span>
<span id="cb1-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># For a self-contained example, we work with the pre-downloaded CSV</span></span>
<span id="cb1-11"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># edgar_ghg &lt;- read_csv("IEA_EDGAR_CO2_1970_2022.csv")</span></span>
<span id="cb1-12"></span>
<span id="cb1-13"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># The file has wide format with one column per year</span></span>
<span id="cb1-14"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Pivot to long and filter to Kenya</span></span>
<span id="cb1-15">kenya_edgar <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> edgar_ghg <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb1-16">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">filter</span>(Country_code_A3 <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"KEN"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb1-17">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">pivot_longer</span>(</span>
<span id="cb1-18">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">cols      =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">matches</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"^Y_</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">\\</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">d{4}$"</span>),</span>
<span id="cb1-19">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">names_to  =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"year"</span>,</span>
<span id="cb1-20">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">values_to =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"emissions_mt_co2e"</span></span>
<span id="cb1-21">  ) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb1-22">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mutate</span>(</span>
<span id="cb1-23">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">year             =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.integer</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">str_remove</span>(year, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"^Y_"</span>)),</span>
<span id="cb1-24">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">emissions_mt_co2e =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.numeric</span>(emissions_mt_co2e)</span>
<span id="cb1-25">  ) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb1-26">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">filter</span>(year <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2000</span>)</span></code></pre></div>
</section>
<section id="primap-hist" class="level3">
<h3 class="anchored" data-anchor-id="primap-hist">PRIMAP-hist</h3>
<p>PRIMAP-hist version 2.5 is available from the Potsdam Institute’s data repository. It covers 1750-2022 and uses UNFCCC submissions as the primary source where available, supplementing with FAO and IEA data for gaps.</p>
<div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb2-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># PRIMAP-hist v2.5: country-level, all sectors, CO2e (AR4 GWPs)</span></span>
<span id="cb2-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Source: https://zenodo.org/records/10705513</span></span>
<span id="cb2-3">primap_url <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">paste0</span>(</span>
<span id="cb2-4">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"https://zenodo.org/records/10705513/files/"</span>,</span>
<span id="cb2-5">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Guetschow-et-al-2024-PRIMAP-hist_v2.5.1_final_no_rounding.csv"</span></span>
<span id="cb2-6">)</span>
<span id="cb2-7"></span>
<span id="cb2-8">primap_raw <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">read_csv</span>(primap_url, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">show_col_types =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">FALSE</span>)</span>
<span id="cb2-9"></span>
<span id="cb2-10">kenya_primap <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> primap_raw <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb2-11">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">filter</span>(</span>
<span id="cb2-12">    country  <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"KEN"</span>,</span>
<span id="cb2-13">    category <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"KYOTOGHG (AR4GWP100)"</span>,  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># All Kyoto gases in CO2e</span></span>
<span id="cb2-14">    scenario <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"HISTCR"</span>                 <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Country-reported priority</span></span>
<span id="cb2-15">  ) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb2-16">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">pivot_longer</span>(</span>
<span id="cb2-17">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">cols      =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">matches</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"^</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">\\</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">d{4}$"</span>),</span>
<span id="cb2-18">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">names_to  =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"year"</span>,</span>
<span id="cb2-19">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">values_to =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"emissions_mt_co2e"</span></span>
<span id="cb2-20">  ) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb2-21">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mutate</span>(</span>
<span id="cb2-22">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">year             =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.integer</span>(year),</span>
<span id="cb2-23">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">emissions_mt_co2e =</span> emissions_mt_co2e <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1000</span>  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Convert kt to Mt</span></span>
<span id="cb2-24">  ) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb2-25">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">filter</span>(year <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2000</span>)</span></code></pre></div>
</section>
</section>
<section id="building-the-progress-chart" class="level2">
<h2 class="anchored" data-anchor-id="building-the-progress-chart">Building the Progress Chart</h2>
<p>With both datasets in hand, the core analytical question is: how does Kenya’s actual emissions trajectory compare to the path implied by the NDC target?</p>
<p>The NDC specifies a 32% reduction from a 2016 baseline. Using the PRIMAP-hist value for 2016 as the reference point, the target path is a straight line from the 2016 baseline to 68% of that value in 2030. This is a simplification: the NDC does not specify an annual trajectory, only a 2030 endpoint. But for visualisation purposes it is a reasonable linear interpolation.</p>
<div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb3-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(ggplot2)</span>
<span id="cb3-2"></span>
<span id="cb3-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Establish the 2016 baseline from PRIMAP-hist</span></span>
<span id="cb3-4">baseline_2016 <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> kenya_primap <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb3-5">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">filter</span>(year <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2016</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb3-6">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">pull</span>(emissions_mt_co2e)</span>
<span id="cb3-7"></span>
<span id="cb3-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># NDC target: 32% below 2016 baseline by 2030</span></span>
<span id="cb3-9">ndc_target_2030 <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> baseline_2016 <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> (<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.32</span>)</span>
<span id="cb3-10"></span>
<span id="cb3-11"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Build the linear target path (2016 to 2030)</span></span>
<span id="cb3-12">target_path <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tibble</span>(</span>
<span id="cb3-13">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">year             =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2016</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2030</span>),</span>
<span id="cb3-14">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">emissions_mt_co2e =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(baseline_2016, ndc_target_2030)</span>
<span id="cb3-15">)</span>
<span id="cb3-16"></span>
<span id="cb3-17"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Most recent data year in PRIMAP-hist</span></span>
<span id="cb3-18">latest_year <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">max</span>(kenya_primap<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>year)</span>
<span id="cb3-19"></span>
<span id="cb3-20"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Chart</span></span>
<span id="cb3-21"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ggplot</span>() <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb3-22">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Historical PRIMAP-hist trajectory</span></span>
<span id="cb3-23">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">geom_line</span>(</span>
<span id="cb3-24">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">data    =</span> kenya_primap,</span>
<span id="cb3-25">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">mapping =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">aes</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">x =</span> year, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">y =</span> emissions_mt_co2e, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">colour =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Actual (PRIMAP-hist)"</span>),</span>
<span id="cb3-26">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">linewidth =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span></span>
<span id="cb3-27">  ) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb3-28">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># NDC target path</span></span>
<span id="cb3-29">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">geom_line</span>(</span>
<span id="cb3-30">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">data     =</span> target_path,</span>
<span id="cb3-31">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">mapping  =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">aes</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">x =</span> year, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">y =</span> emissions_mt_co2e, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">colour =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"NDC target path"</span>),</span>
<span id="cb3-32">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">linetype =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"dashed"</span>,</span>
<span id="cb3-33">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">linewidth =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span></span>
<span id="cb3-34">  ) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb3-35">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Mark the NDC baseline year</span></span>
<span id="cb3-36">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">geom_vline</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">xintercept =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2016</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">colour =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"grey60"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">linetype =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"dotted"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb3-37">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Mark the 2030 target endpoint</span></span>
<span id="cb3-38">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">geom_point</span>(</span>
<span id="cb3-39">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">data    =</span> target_path <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">filter</span>(year <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2030</span>),</span>
<span id="cb3-40">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">mapping =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">aes</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">x =</span> year, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">y =</span> emissions_mt_co2e),</span>
<span id="cb3-41">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">colour  =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#e63946"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">size =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span></span>
<span id="cb3-42">  ) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb3-43">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">annotate</span>(</span>
<span id="cb3-44">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"text"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">x =</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">2030.3</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">y =</span> ndc_target_2030,</span>
<span id="cb3-45">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">label =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">paste0</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">round</span>(ndc_target_2030, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>), <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">" MtCO2e</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">\n</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">(32% reduction target)"</span>),</span>
<span id="cb3-46">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">hjust =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">size =</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">3.2</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">colour =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#e63946"</span></span>
<span id="cb3-47">  ) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb3-48">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">scale_colour_manual</span>(</span>
<span id="cb3-49">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">values =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Actual (PRIMAP-hist)"</span> <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#2d6a4f"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"NDC target path"</span> <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#e63946"</span>)</span>
<span id="cb3-50">  ) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb3-51">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">scale_x_continuous</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">breaks =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">seq</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2000</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2030</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">by =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>)) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb3-52">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">labs</span>(</span>
<span id="cb3-53">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">title    =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Kenya GHG Emissions vs. 2030 NDC Target"</span>,</span>
<span id="cb3-54">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">subtitle =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">paste0</span>(</span>
<span id="cb3-55">      <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Actual trajectory (PRIMAP-hist v2.5) vs. conditional NDC path</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">\n</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>,</span>
<span id="cb3-56">      <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Baseline: 2016 | Target: 32% reduction by 2030"</span></span>
<span id="cb3-57">    ),</span>
<span id="cb3-58">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">x       =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">NULL</span>,</span>
<span id="cb3-59">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">y       =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Total GHG emissions (MtCO2e, AR4)"</span>,</span>
<span id="cb3-60">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">colour  =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">NULL</span>,</span>
<span id="cb3-61">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">caption =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">paste0</span>(</span>
<span id="cb3-62">      <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Source: PRIMAP-hist v2.5 (Gütschow et al., 2024) | "</span>,</span>
<span id="cb3-63">      <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"NDC target: Kenya Updated NDC 2020"</span></span>
<span id="cb3-64">    )</span>
<span id="cb3-65">  ) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb3-66">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">theme_minimal</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">base_size =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">13</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb3-67">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">theme</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">legend.position =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"top"</span>)</span></code></pre></div>
<p>Running this code with current PRIMAP-hist data shows Kenya’s emissions rising from around 60 MtCO2e in 2000 to the 2016 baseline, with upward pressure continuing through the early 2020s. The NDC target path runs sharply downward from 2016. The gap between those two lines is the policy challenge in visual form.</p>
</section>
<section id="what-the-trajectory-actually-shows" class="level2">
<h2 class="anchored" data-anchor-id="what-the-trajectory-actually-shows">What the Trajectory Actually Shows</h2>
<p>PRIMAP-hist data for Kenya through 2022 puts total GHG emissions at roughly 80-85 MtCO2e when land-use change is included. The 32% conditional target, measured against a 73 MtCO2e baseline, implies reaching approximately 50 MtCO2e by 2030.</p>
<p>That is a substantial reversal of a multi-decade upward trend. Kenya’s population grew from 30 million in 2000 to over 55 million by 2025. Agricultural emissions, driven largely by livestock (Kenya has one of Africa’s larger cattle populations), account for roughly 40% of total GHG output. Land-use change from deforestation and agricultural expansion has been a persistent emissions source, partially offset by reforestation efforts in recent years.</p>
<p>The energy sector, by contrast, shows a more favourable story. Kenya’s electricity grid runs on roughly 90% renewable generation, dominated by geothermal from the Olkaria complex. The expansion of solar and wind capacity in recent years has kept the power sector’s emissions growth modest relative to GDP growth. This is the structural advantage Kenya brings to its NDC: it does not need to decarbonise its power grid, which is already cleaner than most middle-income countries.</p>
<p>The harder sectors are transport and agriculture. Transport emissions are rising with vehicle imports and urban congestion. Agriculture, where methane from livestock dominates, is difficult to reduce without affecting food security and rural livelihoods that depend on the livestock economy.</p>
</section>
<section id="the-conditionality-question-in-practice" class="level2">
<h2 class="anchored" data-anchor-id="the-conditionality-question-in-practice">The Conditionality Question in Practice</h2>
<p>The 32% target is explicitly conditional on receiving “adequate, predictable, and sustainable means of implementation from the international community.” Kenya’s unconditional commitment is 3% below the baseline, which amounts to modest efficiency improvements on a business-as-usual path.</p>
<p>What does that conditionality mean in practice? Kenya’s NDC financing needs are estimated at roughly USD 62 billion over the 2020-2030 period for both mitigation and adaptation. That financing must come from a combination of grants, concessional loans, and private climate finance, primarily through mechanisms such as the Green Climate Fund, bilateral climate finance commitments, and voluntary carbon markets.</p>
<p>The tracking challenge is that no public dashboard currently links Kenya’s NDC financing received to emissions reductions achieved. Development finance institutions publish their climate commitments in aggregate. NEMA reports sectoral mitigation actions. But the implied question of the NDC framework, namely whether the international support conditionality is being met at a level that enables the 32% target, is not answered anywhere in a format that a policy analyst can use directly.</p>
<p>This is precisely where a reproducible R workflow adds value. A tracker that combines PRIMAP-hist emissions data, Kenya’s Biennial Transparency Reports, and GCF and bilateral disbursement data (available through the OECD DAC Creditor Reporting System) could produce an annual progress assessment. None of those datasets requires special access. All of them can be read into R and updated when new vintages are published.</p>
</section>
<section id="building-a-repeatable-tracker" class="level2">
<h2 class="anchored" data-anchor-id="building-a-repeatable-tracker">Building a Repeatable Tracker</h2>
<p>The workflow outlined above handles the emissions side. Adding the finance side requires one more data source: the OECD CRS database, which tracks climate-tagged development finance by recipient country.</p>
<div class="sourceCode" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb4-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># OECD Creditor Reporting System: climate-tagged flows to Kenya</span></span>
<span id="cb4-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Available at: https://stats.oecd.org/DownloadFiles.aspx?DatasetCode=CRS1</span></span>
<span id="cb4-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Filter: recipient = Kenya, purpose codes related to energy and environment</span></span>
<span id="cb4-4"></span>
<span id="cb4-5">oecd_crs <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">read_csv</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"CRS_2023_download.csv"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">show_col_types =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">FALSE</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb4-6">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">filter</span>(</span>
<span id="cb4-7">    RecipientName <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Kenya"</span>,</span>
<span id="cb4-8">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">str_detect</span>(PurposeName, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Climate|Renewable|Forest|Environment"</span>)</span>
<span id="cb4-9">  ) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb4-10">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">summarise</span>(</span>
<span id="cb4-11">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">climate_finance_usd_m =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sum</span>(USD_Disbursement, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">na.rm =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>),</span>
<span id="cb4-12">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">.by =</span> Year</span>
<span id="cb4-13">  )</span>
<span id="cb4-14"></span>
<span id="cb4-15"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Join with emissions trajectory for a combined tracker</span></span>
<span id="cb4-16">ndc_tracker <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> kenya_primap <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb4-17">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">left_join</span>(oecd_crs, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">by =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"year"</span> <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Year"</span>)) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb4-18">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mutate</span>(</span>
<span id="cb4-19">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">target_path_mt =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">case_when</span>(</span>
<span id="cb4-20">      year <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2016</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">~</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">NA_real_</span>,</span>
<span id="cb4-21">      year <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span>  <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2016</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">~</span> baseline_2016 <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> (baseline_2016 <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> ndc_target_2030) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span></span>
<span id="cb4-22">                     ((year <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2016</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> (<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2030</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2016</span>))</span>
<span id="cb4-23">    ),</span>
<span id="cb4-24">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">gap_to_target =</span> emissions_mt_co2e <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> target_path_mt</span>
<span id="cb4-25">  )</span></code></pre></div>
<p>This structure gives you a single data frame with actual emissions, the implied target path for each year, the gap between them, and the climate finance received. Rendered through a Quarto document, it becomes an annually updatable NDC progress report that can be shared with government counterparts, development partners, or parliamentary committees.</p>
<p>For organisations already using R for <a href="../../../blog/posts/esia-reproducibility/">environmental assessments</a> or <a href="../../../blog/posts/kenya-open-data/">Kenya open data</a> work, extending this to NDC tracking requires no new infrastructure. The same reproducibility principles that apply to EIA reporting apply here: version-controlled code, documented data sources, transparent assumptions.</p>
</section>
<section id="the-analysts-who-should-build-this" class="level2">
<h2 class="anchored" data-anchor-id="the-analysts-who-should-build-this">The Analysts Who Should Build This</h2>
<p>Climate policy professionals in Kenya’s Ministry of Environment, NEMA, and the National Treasury work with NDC commitments regularly. Development finance analysts at the African Development Bank, KfW, and bilateral donors need progress data to justify continued climate finance. NGO researchers at organisations like the African Centre for Technology Studies produce independent assessments that would benefit from a common data infrastructure.</p>
<p>None of these groups currently has a shared, reproducible tracking framework. The UNFCCC’s own transparency portal provides raw data but no country-specific analysis. The Climate Action Tracker covers Kenya but updates infrequently and does not expose its underlying calculations.</p>
<p>The tools to build this exist today. The databases are public. The analytical methods are straightforward. What is missing is the institutional commitment to treat NDC tracking as a data engineering problem, not just a reporting obligation.</p>
<p>If your organisation monitors Kenya’s climate commitments and you are building something along these lines, what data source has been your biggest obstacle? The comments are open, and so is our <a href="../../../contact.html">contact page</a>.</p>
<p>Kwiz Computing Technologies provides climate analytics services for development finance institutions, government agencies, and NGOs working on Kenya and East Africa climate policy. Our work on <a href="../../../blog/posts/carbon-markets-quant-methods/">carbon markets</a> and ESG analytics uses the same open data infrastructure described here.</p>


</section>

 ]]></description>
  <category>environmental-analytics</category>
  <category>climate-policy</category>
  <guid>https://kwizresearch.com/blog/posts/kenya-ndc-climate-commitments-tracking-open-data/</guid>
  <pubDate>Thu, 23 Apr 2026 00:00:00 GMT</pubDate>
  <media:content url="https://kwizresearch.com/blog/posts/kenya-ndc-climate-commitments-tracking-open-data/thumbnail.png" medium="image" type="image/png"/>
</item>
<item>
  <title>Position Sizing for Forex: Kelly Criterion in R</title>
  <dc:creator>Kwiz Computing Technologies</dc:creator>
  <link>https://kwizresearch.com/blog/posts/position-sizing-kelly-criterion-forex-r/</link>
  <description><![CDATA[ 




<p>Traders spend months tuning entry signals and five minutes deciding how many lots to trade. That imbalance is a mistake, because position sizing determines the shape of your equity curve far more than any entry condition does.</p>
<p>This is not opinion. It follows directly from the mathematics of compound growth. Two traders running the same signals but different sizing rules will produce completely different account trajectories over 500 trades. This article implements the Kelly criterion in R so you can find the sizing that maximises long-run growth from your actual backtest data.</p>
<section id="why-sizing-dominates-entry-signals" class="level2">
<h2 class="anchored" data-anchor-id="why-sizing-dominates-entry-signals">Why Sizing Dominates Entry Signals</h2>
<p>Consider a coin-flip strategy: 55% win rate, pays 1:1. Trade it at 50% of capital per flip and you will almost certainly go broke, because a short losing streak wipes you out before the edge compounds. Trade it at 1% and you survive but grow slowly. Trade it at the Kelly-optimal 10% and geometric growth is maximised.</p>
<p>Mathematically, the long-run compound growth rate of a strategy is:</p>
<p><img src="https://latex.codecogs.com/png.latex?g%20=%20%5Cln(1%20+%20f%20%5Ccdot%20b)%20%5Ccdot%20p%20+%20%5Cln(1%20-%20f)%20%5Ccdot%20(1%20-%20p)"></p>
<p>where <code>f</code> is the fraction of capital risked, <code>b</code> is the win-to-loss ratio, and <code>p</code> is the win rate. This expression has a maximum at a specific <code>f</code>. Above that maximum, growth falls, and well above it, ruin becomes certain. That maximum is the Kelly fraction.</p>
<p>A systematic forex trader in Nairobi running a 52% win rate breakout strategy on EURUSD has an edge. Whether that edge translates into account growth depends almost entirely on how they size positions.</p>
</section>
<section id="the-kelly-criterion-formula-and-derivation" class="level2">
<h2 class="anchored" data-anchor-id="the-kelly-criterion-formula-and-derivation">The Kelly Criterion: Formula and Derivation</h2>
<p>John Kelly derived his formula in 1956 to solve optimal bet sizing for a gambler with a noisy signal. For a binary outcome game (win <code>b</code> units or lose 1 unit per bet), the Kelly fraction is:</p>
<p><img src="https://latex.codecogs.com/png.latex?f%5E*%20=%20%5Cfrac%7Bp%20%5Ccdot%20b%20-%20(1%20-%20p)%7D%7Bb%7D%20=%20p%20-%20%5Cfrac%7Bq%7D%7Bb%7D"></p>
<p>where <code>p</code> is win probability and <code>q = 1 - p</code>. The formula tells you the fraction of current capital to risk on each trade. For a strategy with 55% wins and average payoff ratio 1.5:</p>
<p><img src="https://latex.codecogs.com/png.latex?f%5E*%20=%200.55%20-%20%5Cfrac%7B0.45%7D%7B1.5%7D%20=%200.55%20-%200.30%20=%200.25"></p>
<p>Risk 25% of equity per trade. That is the full Kelly recommendation for those parameters, and it is almost certainly too aggressive for a live forex account. We will get to why shortly.</p>
</section>
<section id="estimating-parameters-and-computing-kelly-in-r" class="level2">
<h2 class="anchored" data-anchor-id="estimating-parameters-and-computing-kelly-in-r">Estimating Parameters and Computing Kelly in R</h2>
<p>Before sizing, you need reliable estimates of <code>p</code> and <code>b</code> from real trade data. The following R code reads an MT5-style trade log and computes both parameters.</p>
<div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb1-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(dplyr)</span>
<span id="cb1-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(readr)</span>
<span id="cb1-3"></span>
<span id="cb1-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># MT5 exports a semicolon-delimited file; adjust sep as needed</span></span>
<span id="cb1-5">trades <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">read_delim</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"mt5_history.csv"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">delim =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">";"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">col_types =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">cols</span>()) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb1-6">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">rename_with</span>(tolower) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb1-7">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">filter</span>(type <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%in%</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"buy"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"sell"</span>)) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span>    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># exclude deposits/withdrawals</span></span>
<span id="cb1-8">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mutate</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">profit =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.numeric</span>(profit))</span>
<span id="cb1-9"></span>
<span id="cb1-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Separate wins from losses</span></span>
<span id="cb1-11">wins  <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> trades <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">filter</span>(profit <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)</span>
<span id="cb1-12">losses <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> trades <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">filter</span>(profit <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)</span>
<span id="cb1-13"></span>
<span id="cb1-14">win_rate   <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">nrow</span>(wins) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">nrow</span>(trades)</span>
<span id="cb1-15">avg_win    <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mean</span>(wins<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>profit)</span>
<span id="cb1-16">avg_loss   <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">abs</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mean</span>(losses<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>profit))</span>
<span id="cb1-17">payoff_ratio <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> avg_win <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> avg_loss</span>
<span id="cb1-18"></span>
<span id="cb1-19"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">cat</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sprintf</span>(</span>
<span id="cb1-20">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Trades: %d | Win rate: %.1f%% | Avg win: %.2f | Avg loss: %.2f | Payoff: %.2f</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">\n</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>,</span>
<span id="cb1-21">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">nrow</span>(trades), win_rate <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100</span>, avg_win, avg_loss, payoff_ratio</span>
<span id="cb1-22">))</span></code></pre></div>
<p>For real East African forex traders operating on ECN brokers through Nairobi-based prop desks, the average trade log contains 200 to 800 trades per year. That is enough data to estimate win rate with reasonable precision, but not enough to treat the estimate as exact. We return to this limitation in the next section.</p>
<section id="from-parameters-to-lot-size" class="level3">
<h3 class="anchored" data-anchor-id="from-parameters-to-lot-size">From Parameters to Lot Size</h3>
<p>With <code>p</code> and <code>b</code> in hand, the Kelly fraction is a single line of arithmetic:</p>
<div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb2-1">kelly_fraction <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(win_rate, payoff_ratio) {</span>
<span id="cb2-2">  q <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> win_rate</span>
<span id="cb2-3">  (win_rate <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> payoff_ratio <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> q) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> payoff_ratio</span>
<span id="cb2-4">}</span>
<span id="cb2-5"></span>
<span id="cb2-6">f_star <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">kelly_fraction</span>(win_rate, payoff_ratio)</span>
<span id="cb2-7"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">cat</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sprintf</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Full Kelly fraction: %.4f (%.1f%% of equity per trade)</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">\n</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>,</span>
<span id="cb2-8">            f_star, f_star <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100</span>))</span></code></pre></div>
<p>To convert that fraction into an MT5 lot size for a given account balance and instrument:</p>
<div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb3-1">lot_size_from_kelly <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(balance_usd, kelly_f, pip_value, stop_loss_pips) {</span>
<span id="cb3-2">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># kelly_f is fraction of equity to risk</span></span>
<span id="cb3-3">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># pip_value in USD per standard lot</span></span>
<span id="cb3-4">  risk_usd  <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> balance_usd <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> kelly_f</span>
<span id="cb3-5">  lots      <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> risk_usd <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> (stop_loss_pips <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> pip_value)</span>
<span id="cb3-6">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">round</span>(lots, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb3-7">}</span>
<span id="cb3-8"></span>
<span id="cb3-9"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Example: $10,000 account, EURUSD, 10 pip SL, pip value = $10/lot</span></span>
<span id="cb3-10"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">lot_size_from_kelly</span>(</span>
<span id="cb3-11">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">balance_usd    =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10000</span>,</span>
<span id="cb3-12">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">kelly_f        =</span> f_star,</span>
<span id="cb3-13">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">pip_value      =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>,</span>
<span id="cb3-14">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">stop_loss_pips =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span></span>
<span id="cb3-15">)</span></code></pre></div>
<p>This gives a concrete lot size you can drop into an Expert Advisor or a manual order.</p>
</section>
</section>
<section id="why-full-kelly-is-almost-always-too-aggressive" class="level2">
<h2 class="anchored" data-anchor-id="why-full-kelly-is-almost-always-too-aggressive">Why Full Kelly Is Almost Always Too Aggressive</h2>
<p>The Kelly fraction is extremely sensitive to the input estimates. A win rate of 55% yields a Kelly fraction well above zero. A win rate of 49%, estimated from a different 300-trade sample of the same strategy, produces a negative Kelly fraction: do not trade. That gap between estimates is entirely plausible due to sampling noise.</p>
<p>This is the core problem. Full Kelly sized to your point estimate of <code>p</code> and <code>b</code> is almost always too aggressive, because your estimates contain error. The resulting over-sizing produces severe drawdowns that compound faster than your edge can recover them.</p>
<p>Bootstrap confidence intervals make this concrete. You can resample your trade history to see how much the Kelly fraction bounces around:</p>
<div class="sourceCode" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb4-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(rsample)</span>
<span id="cb4-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(purrr)</span>
<span id="cb4-3"></span>
<span id="cb4-4">bootstrap_kelly <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(trades, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">n_boot =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2000</span>) {</span>
<span id="cb4-5">  boot_samples <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">bootstraps</span>(trades, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">times =</span> n_boot)</span>
<span id="cb4-6"></span>
<span id="cb4-7">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">map_dbl</span>(boot_samples<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>splits, <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(split) {</span>
<span id="cb4-8">    d      <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">analysis</span>(split)</span>
<span id="cb4-9">    wins   <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> d <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">filter</span>(profit <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)</span>
<span id="cb4-10">    losses <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> d <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">filter</span>(profit <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)</span>
<span id="cb4-11"></span>
<span id="cb4-12">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> (<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">nrow</span>(wins) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">||</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">nrow</span>(losses) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>) <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">return</span>(<span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">NA_real_</span>)</span>
<span id="cb4-13"></span>
<span id="cb4-14">    p_b <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">nrow</span>(wins) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">nrow</span>(d)</span>
<span id="cb4-15">    b_b <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mean</span>(wins<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>profit) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">abs</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mean</span>(losses<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>profit))</span>
<span id="cb4-16">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">kelly_fraction</span>(p_b, b_b)</span>
<span id="cb4-17">  }) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb4-18">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">na.omit</span>()</span>
<span id="cb4-19">}</span>
<span id="cb4-20"></span>
<span id="cb4-21">boot_kelly <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">bootstrap_kelly</span>(trades)</span>
<span id="cb4-22"></span>
<span id="cb4-23"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">cat</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sprintf</span>(</span>
<span id="cb4-24">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Kelly fraction: point est = %.3f | 5th pct = %.3f | 95th pct = %.3f</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">\n</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>,</span>
<span id="cb4-25">  f_star,</span>
<span id="cb4-26">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">quantile</span>(boot_kelly, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.05</span>),</span>
<span id="cb4-27">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">quantile</span>(boot_kelly, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.95</span>)</span>
<span id="cb4-28">))</span>
<span id="cb4-29"></span>
<span id="cb4-30"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Plot the bootstrap distribution</span></span>
<span id="cb4-31"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">hist</span>(boot_kelly,</span>
<span id="cb4-32">     <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">breaks =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">50</span>,</span>
<span id="cb4-33">     <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">main   =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Bootstrap Distribution of Kelly Fraction"</span>,</span>
<span id="cb4-34">     <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">xlab   =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Kelly f*"</span>,</span>
<span id="cb4-35">     <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">col    =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#4E79A7"</span>,</span>
<span id="cb4-36">     <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">border =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"white"</span>)</span>
<span id="cb4-37"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">abline</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">v =</span> f_star, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">col =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"red"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">lwd =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">lty =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb4-38"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">abline</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">v =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">quantile</span>(boot_kelly, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.05</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.95</span>)), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">col =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"orange"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">lwd =</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.5</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">lty =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>)</span></code></pre></div>
<p>When a strategy has 300 trades, the 90% bootstrap interval for the Kelly fraction typically spans 15 percentage points or more. Sizing to the upper end of that range is ruin territory. This is why practitioners universally apply a fractional Kelly.</p>
<p>See the related discussion on overfitting and estimation bias in our post on <a href="../../../blog/posts/combinatorial-purged-cv/">combinatorial purged cross-validation</a>, which applies the same resampling logic to strategy selection.</p>
</section>
<section id="half-kelly-and-fractional-kelly-in-practice" class="level2">
<h2 class="anchored" data-anchor-id="half-kelly-and-fractional-kelly-in-practice">Half-Kelly and Fractional Kelly in Practice</h2>
<p>The solution is to trade a fixed fraction of the Kelly recommendation. Half-Kelly (f = 0.5 * f*) is the most common choice, and it has attractive theoretical properties: it retains roughly 75% of the full Kelly growth rate while halving the variance of outcomes and reducing the severity of drawdowns substantially.</p>
<div class="sourceCode" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb5-1">compare_sizing_methods <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(trades, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">account_size =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10000</span>,</span>
<span id="cb5-2">                                   <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">pip_value =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">stop_loss_pips =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>) {</span>
<span id="cb5-3">  wins   <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> trades <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">filter</span>(profit <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)</span>
<span id="cb5-4">  losses <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> trades <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">filter</span>(profit <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)</span>
<span id="cb5-5"></span>
<span id="cb5-6">  p_est <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">nrow</span>(wins) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">nrow</span>(trades)</span>
<span id="cb5-7">  b_est <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mean</span>(wins<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>profit) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">abs</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mean</span>(losses<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>profit))</span>
<span id="cb5-8">  f_k   <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">kelly_fraction</span>(p_est, b_est)</span>
<span id="cb5-9"></span>
<span id="cb5-10">  methods <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tibble</span>(</span>
<span id="cb5-11">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">method       =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Full Kelly"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Half Kelly"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Fixed 2%"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Fixed 1%"</span>),</span>
<span id="cb5-12">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">fraction     =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(f_k, f_k <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.02</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.01</span>),</span>
<span id="cb5-13">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">lots =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">map_dbl</span>(fraction, \(f)</span>
<span id="cb5-14">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">lot_size_from_kelly</span>(account_size, f, pip_value, stop_loss_pips))</span>
<span id="cb5-15">  )</span>
<span id="cb5-16"></span>
<span id="cb5-17">  methods</span>
<span id="cb5-18">}</span>
<span id="cb5-19"></span>
<span id="cb5-20">sizing_table <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">compare_sizing_methods</span>(trades)</span>
<span id="cb5-21"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">print</span>(sizing_table)</span></code></pre></div>
<p>The fixed 1% and 2% rules are simple and widely used, including by many prop firms and retail brokers operating in East Africa. They work, but they are arbitrary. If your strategy has a Kelly fraction of 4%, the 1% rule is overly conservative and leaves growth on the table. If your Kelly fraction is 1%, the 2% rule is over-leveraged.</p>
<p>Fractional Kelly anchors your sizing to your actual edge rather than an arbitrary number.</p>
<section id="simulating-growth-trajectories" class="level3">
<h3 class="anchored" data-anchor-id="simulating-growth-trajectories">Simulating Growth Trajectories</h3>
<p>Simulating forward shows the practical difference between methods. The following code runs a Monte Carlo on your estimated trade distribution:</p>
<div class="sourceCode" id="cb6" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb6-1">simulate_equity <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(trades, fraction, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">n_trades =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">500</span>,</span>
<span id="cb6-2">                            <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">n_sims =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">200</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">start_equity =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10000</span>) {</span>
<span id="cb6-3">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Normalise profits to fraction-of-equity units</span></span>
<span id="cb6-4">  wins_pct   <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> trades <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">filter</span>(profit <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">pull</span>(profit) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> start_equity</span>
<span id="cb6-5">  losses_pct <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> trades <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">filter</span>(profit <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">pull</span>(profit) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> start_equity</span>
<span id="cb6-6"></span>
<span id="cb6-7">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">replicate</span>(n_sims, {</span>
<span id="cb6-8">    equity <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> start_equity</span>
<span id="cb6-9">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">for</span> (i <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">in</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">seq_len</span>(n_trades)) {</span>
<span id="cb6-10">      <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> (<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">runif</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span> win_rate) {</span>
<span id="cb6-11">        r <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sample</span>(wins_pct, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb6-12">      } <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">else</span> {</span>
<span id="cb6-13">        r <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sample</span>(losses_pct, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb6-14">      }</span>
<span id="cb6-15">      equity <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> equity <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> (<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> fraction <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sign</span>(r) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">abs</span>(r) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span></span>
<span id="cb6-16">                            (<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">abs</span>(trades<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>profit <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mean</span>())))</span>
<span id="cb6-17">      <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> (equity <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>) { equity <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>; <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">break</span> }</span>
<span id="cb6-18">    }</span>
<span id="cb6-19">    equity</span>
<span id="cb6-20">  })</span>
<span id="cb6-21">}</span>
<span id="cb6-22"></span>
<span id="cb6-23"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">set.seed</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">42</span>)</span>
<span id="cb6-24">results <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(</span>
<span id="cb6-25">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">full_kelly  =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">simulate_equity</span>(trades, f_star),</span>
<span id="cb6-26">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">half_kelly  =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">simulate_equity</span>(trades, f_star <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>),</span>
<span id="cb6-27">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">fixed_2pct  =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">simulate_equity</span>(trades, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.02</span>),</span>
<span id="cb6-28">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">fixed_1pct  =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">simulate_equity</span>(trades, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.01</span>)</span>
<span id="cb6-29">)</span>
<span id="cb6-30"></span>
<span id="cb6-31"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Median terminal equity per method</span></span>
<span id="cb6-32"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sapply</span>(results, median)</span></code></pre></div>
<p>Across typical East African retail forex strategy parameters (win rates 50-58%, payoff ratios 1.2-1.8), half-Kelly consistently outperforms fixed rules on both median growth and ruin rate over 500-trade horizons. Full Kelly produces the highest median but with dramatically fatter left tails.</p>
<p>This connects to the broader framework for evaluating whether your strategy has genuine edge in the first place, covered in our <a href="../../../blog/posts/deflated-sharpe-ratio/">Deflated Sharpe Ratio</a> post. Position sizing on a false edge accelerates ruin rather than growth.</p>
</section>
<section id="live-mt5-integration" class="level3">
<h3 class="anchored" data-anchor-id="live-mt5-integration">Live MT5 Integration</h3>
<p>For traders running Expert Advisors on MetaTrader 5, the Kelly lot calculation needs to happen at order submission time, using current account equity rather than initial capital. A clean pattern:</p>
<div class="sourceCode" id="cb7" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb7-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Run this from a Plumber API endpoint called by the EA before order submission</span></span>
<span id="cb7-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># See our MT5-R bridge architecture in the systematic trading post</span></span>
<span id="cb7-3"></span>
<span id="cb7-4">current_equity <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">12500</span>  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># fetched from MT5 account info</span></span>
<span id="cb7-5">kelly_lots <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">lot_size_from_kelly</span>(</span>
<span id="cb7-6">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">balance_usd    =</span> current_equity,</span>
<span id="cb7-7">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">kelly_f        =</span> f_star <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>,    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># half-Kelly</span></span>
<span id="cb7-8">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">pip_value      =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>,</span>
<span id="cb7-9">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">stop_loss_pips =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">15</span>             <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># from strategy signal</span></span>
<span id="cb7-10">)</span>
<span id="cb7-11"></span>
<span id="cb7-12"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Return to EA</span></span>
<span id="cb7-13"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">recommended_lots =</span> kelly_lots, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">method =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"half-kelly"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">fraction =</span> f_star <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span></code></pre></div>
<p>This pattern updates position sizing dynamically as account equity changes, which is exactly what Kelly requires. A growing account takes larger positions; a shrinking account cuts back automatically. For a deeper look at building R-based trading infrastructure that connects to MT5, see <a href="../../../blog/posts/systematic-trading-r/">Systematic Trading Infrastructure in R</a>.</p>
</section>
</section>
<section id="the-bottom-line" class="level2">
<h2 class="anchored" data-anchor-id="the-bottom-line">The Bottom Line</h2>
<p>Kelly sizing is not a silver bullet. It requires honest estimates of your edge, bootstrap intervals to understand estimation uncertainty, and the discipline to apply fractional Kelly rather than the full formula. Applied properly, it gives you a principled answer to the question that matters most in long-run trading: not which direction to trade, but how much.</p>
<p>The traders on Nairobi’s retail forex desks who run consistent edge lose that edge to poor sizing more often than to poor signals. If you have a backtested strategy with documented win rate and payoff data, run the numbers. A 15-minute R script will tell you whether your current sizing is leaving growth on the table or pushing you toward ruin.</p>
<p>What does your bootstrap Kelly interval look like? If you share your trade log, the Kwiz Quants team can run a full sizing analysis as part of our strategy evaluation service.</p>


</section>

 ]]></description>
  <category>quantitative-finance</category>
  <category>systematic-trading</category>
  <guid>https://kwizresearch.com/blog/posts/position-sizing-kelly-criterion-forex-r/</guid>
  <pubDate>Thu, 23 Apr 2026 00:00:00 GMT</pubDate>
  <media:content url="https://kwizresearch.com/blog/posts/position-sizing-kelly-criterion-forex-r/thumbnail.png" medium="image" type="image/png"/>
</item>
<item>
  <title>R Shiny Trading Dashboard for Kenyan Quant Traders</title>
  <dc:creator>Kwiz Computing Technologies</dc:creator>
  <link>https://kwizresearch.com/blog/posts/kenyan-quant-traders-production-strategy-dashboards-r-shiny/</link>
  <description><![CDATA[ 




<section id="your-backtested-strategy-looked-great.-then-live-trading-happened." class="level2">
<h2 class="anchored" data-anchor-id="your-backtested-strategy-looked-great.-then-live-trading-happened.">Your Backtested Strategy Looked Great. Then Live Trading Happened.</h2>
<p>Most Kenyan retail forex traders running systematic strategies on MetaTrader 5 discover the same problem at the worst possible time: the backtest looked clean, but three weeks of live execution reveal a drawdown their spreadsheet never surfaced. The monitoring gap is not a trading problem. It is an engineering problem, and R/Shiny solves it for a fraction of institutional infrastructure costs.</p>
<p>Kenya’s retail forex community is one of the most active in Sub-Saharan Africa. The Capital Markets Authority estimates that retail forex trading volumes have grown significantly since the 2017 amendment that tightened broker regulation. Traders here are sophisticated: many run MT5 Expert Advisors, export trade histories to CSV, and build their own Excel models. But Excel is not a monitoring system. It does not stream data, flag regime changes, or compute drawdown metrics in real time. A Rhino-structured Shiny application does all three, and you can have the first version running in a weekend.</p>
</section>
<section id="what-rhino-adds-to-a-production-shiny-app" class="level2">
<h2 class="anchored" data-anchor-id="what-rhino-adds-to-a-production-shiny-app">What Rhino Adds to a Production Shiny App</h2>
<p>Rhino is an application framework developed by Appsilon for building Shiny apps with engineering-grade structure. Without it, Shiny projects tend to accumulate logic in <code>server.R</code> until the file becomes impossible to test or maintain. Rhino enforces a module system, separates UI from logic, and integrates testthat from the start.</p>
<p>A Rhino project for a trading dashboard has this directory layout:</p>
<pre><code>trading-dashboard/
├── app/
│   ├── main.R               # Entry point
│   ├── logic/
│   │   ├── trade_log.R      # Data ingestion and cleaning
│   │   ├── drawdown.R       # Drawdown computation
│   │   └── regime.R         # Regime detection logic
│   └── view/
│       ├── overview.R       # Overview tab UI + server
│       ├── drawdown.R       # Drawdown tab UI + server
│       └── regime.R         # Regime tab UI + server
├── tests/
│   └── testthat/
│       └── test-drawdown.R  # Unit tests for risk metrics
├── renv.lock
└── rhino.yml</code></pre>
<p>The <code>logic/</code> layer is pure R: no Shiny dependencies, fully testable. The <code>view/</code> layer contains Shiny modules that consume the logic layer’s output. This separation is what makes the dashboard maintainable once it reaches production.</p>
</section>
<section id="ingesting-mt5-trade-logs" class="level2">
<h2 class="anchored" data-anchor-id="ingesting-mt5-trade-logs">Ingesting MT5 Trade Logs</h2>
<p>MT5 exports a structured HTML or CSV report from the terminal’s “Account History” section. The CSV variant is easier to parse programmatically. A typical MT5 export has columns for ticket number, open time, close time, symbol, type, lots, open price, close price, and profit.</p>
<p>The ingestion module lives in <code>app/logic/trade_log.R</code>:</p>
<div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb2-1">box<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">use</span>(</span>
<span id="cb2-2">  dplyr[...],</span>
<span id="cb2-3">  readr[read_csv, cols, col_character, col_double, col_datetime],</span>
<span id="cb2-4">  lubridate[ymd_hms, with_tz]</span>
<span id="cb2-5">)</span>
<span id="cb2-6"></span>
<span id="cb2-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' Load and clean an MT5 trade history CSV</span></span>
<span id="cb2-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#'</span></span>
<span id="cb2-9"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @param path Path to the exported CSV file</span></span>
<span id="cb2-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @param tz   Timezone of the MT5 server (e.g. "Africa/Nairobi")</span></span>
<span id="cb2-11"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @return A tibble with one row per closed trade</span></span>
<span id="cb2-12">load_mt5_trades <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(path, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">tz =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Africa/Nairobi"</span>) {</span>
<span id="cb2-13">  raw <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">read_csv</span>(path, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">col_types =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">cols</span>(</span>
<span id="cb2-14">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">Ticket     =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">col_double</span>(),</span>
<span id="cb2-15">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">OpenTime   =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">col_character</span>(),</span>
<span id="cb2-16">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">CloseTime  =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">col_character</span>(),</span>
<span id="cb2-17">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">Symbol     =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">col_character</span>(),</span>
<span id="cb2-18">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">Type       =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">col_character</span>(),</span>
<span id="cb2-19">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">Lots       =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">col_double</span>(),</span>
<span id="cb2-20">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">OpenPrice  =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">col_double</span>(),</span>
<span id="cb2-21">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">ClosePrice =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">col_double</span>(),</span>
<span id="cb2-22">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">Profit     =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">col_double</span>()</span>
<span id="cb2-23">  ))</span>
<span id="cb2-24"></span>
<span id="cb2-25">  raw <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb2-26">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">filter</span>(Type <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%in%</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"buy"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"sell"</span>)) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb2-27">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mutate</span>(</span>
<span id="cb2-28">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">open_time  =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ymd_hms</span>(OpenTime)  <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">with_tz</span>(tz),</span>
<span id="cb2-29">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">close_time =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ymd_hms</span>(CloseTime) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">with_tz</span>(tz),</span>
<span id="cb2-30">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">duration_hrs =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.numeric</span>(close_time <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> open_time, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">units =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"hours"</span>)</span>
<span id="cb2-31">    ) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb2-32">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">select</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">ticket =</span> Ticket, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">symbol =</span> Symbol, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> Type, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">lots =</span> Lots,</span>
<span id="cb2-33">           <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">open_price =</span> OpenPrice, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">close_price =</span> ClosePrice,</span>
<span id="cb2-34">           <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">profit =</span> Profit, open_time, close_time, duration_hrs) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb2-35">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">arrange</span>(close_time)</span>
<span id="cb2-36">}</span></code></pre></div>
<p>The <code>box::use()</code> call at the top is Rhino’s preferred import style: explicit, testable, and collision-free. Setting the timezone to <code>"Africa/Nairobi"</code> (EAT, UTC+3) matters for any analysis involving trading sessions, because session-based performance metrics behave differently when timestamps are in the broker’s server time versus your local time.</p>
</section>
<section id="the-drawdown-module" class="level2">
<h2 class="anchored" data-anchor-id="the-drawdown-module">The Drawdown Module</h2>
<p>Drawdown is where most retail monitoring tools fall short. A broker dashboard shows open equity, but it does not show you the maximum drawdown from peak over your last 200 trades, the time spent in drawdown, or whether the current drawdown is within historical norms for your system.</p>
<p>The computation lives in <code>app/logic/drawdown.R</code>:</p>
<div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb3-1">box<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">use</span>(dplyr[...], tibble[tibble])</span>
<span id="cb3-2"></span>
<span id="cb3-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' Compute running drawdown metrics from a trade sequence</span></span>
<span id="cb3-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#'</span></span>
<span id="cb3-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @param trades A tibble from load_mt5_trades()</span></span>
<span id="cb3-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @param initial_balance Starting account balance in USD</span></span>
<span id="cb3-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @return A tibble with equity curve and drawdown columns</span></span>
<span id="cb3-8">compute_drawdown <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(trades, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">initial_balance =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10000</span>) {</span>
<span id="cb3-9">  trades <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb3-10">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">arrange</span>(close_time) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb3-11">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mutate</span>(</span>
<span id="cb3-12">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">cumulative_pnl =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">cumsum</span>(profit),</span>
<span id="cb3-13">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">equity         =</span> initial_balance <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> cumulative_pnl,</span>
<span id="cb3-14">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">running_peak   =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">cummax</span>(equity),</span>
<span id="cb3-15">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">drawdown_abs   =</span> running_peak <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> equity,</span>
<span id="cb3-16">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">drawdown_pct   =</span> drawdown_abs <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> running_peak</span>
<span id="cb3-17">    )</span>
<span id="cb3-18">}</span>
<span id="cb3-19"></span>
<span id="cb3-20"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' Summarise key drawdown statistics</span></span>
<span id="cb3-21"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#'</span></span>
<span id="cb3-22"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @param dd_tbl Output from compute_drawdown()</span></span>
<span id="cb3-23"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @return A named list of summary statistics</span></span>
<span id="cb3-24">drawdown_summary <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(dd_tbl) {</span>
<span id="cb3-25">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(</span>
<span id="cb3-26">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">max_drawdown_pct  =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">max</span>(dd_tbl<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>drawdown_pct),</span>
<span id="cb3-27">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">max_drawdown_abs  =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">max</span>(dd_tbl<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>drawdown_abs),</span>
<span id="cb3-28">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">current_dd_pct    =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tail</span>(dd_tbl<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>drawdown_pct, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>),</span>
<span id="cb3-29">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">trades_in_dd      =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sum</span>(dd_tbl<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>drawdown_pct <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>),</span>
<span id="cb3-30">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">recovery_factor   =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tail</span>(dd_tbl<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>cumulative_pnl, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">max</span>(dd_tbl<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>drawdown_abs)</span>
<span id="cb3-31">  )</span>
<span id="cb3-32">}</span></code></pre></div>
<p>The recovery factor, total net profit divided by maximum drawdown, gives a single number that captures whether the system earns enough to justify the pain of its worst losing streak. A recovery factor below 1 means the system has not yet earned back its worst drawdown in total profits. Most retail traders do not track this number at all.</p>
<p>The Shiny module that renders this in the dashboard (<code>app/view/drawdown.R</code>) uses <code>echarts4r</code> for the equity curve chart, which renders cleanly on mobile browsers, a practical requirement for traders monitoring positions from a phone:</p>
<div class="sourceCode" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb4-1">box<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">use</span>(shiny[...], echarts4r[...])</span>
<span id="cb4-2">box<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">use</span>(app<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span>logic<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span>drawdown[compute_drawdown, drawdown_summary])</span>
<span id="cb4-3"></span>
<span id="cb4-4">drawdown_ui <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(id) {</span>
<span id="cb4-5">  ns <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">NS</span>(id)</span>
<span id="cb4-6">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tagList</span>(</span>
<span id="cb4-7">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">echarts4rOutput</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ns</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"equity_curve"</span>)),</span>
<span id="cb4-8">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">verbatimTextOutput</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ns</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"dd_stats"</span>))</span>
<span id="cb4-9">  )</span>
<span id="cb4-10">}</span>
<span id="cb4-11"></span>
<span id="cb4-12">drawdown_server <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(id, trades, initial_balance) {</span>
<span id="cb4-13">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">moduleServer</span>(id, <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(input, output, session) {</span>
<span id="cb4-14">    dd_data <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">reactive</span>({</span>
<span id="cb4-15">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">compute_drawdown</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">trades</span>(), <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">initial_balance</span>())</span>
<span id="cb4-16">    })</span>
<span id="cb4-17"></span>
<span id="cb4-18">    output<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>equity_curve <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">renderEcharts4r</span>({</span>
<span id="cb4-19">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">dd_data</span>() <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb4-20">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">e_charts</span>(close_time) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb4-21">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">e_line</span>(equity, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">name =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Equity"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb4-22">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">e_line</span>(running_peak, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">name =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Peak"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">lineStyle =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"dashed"</span>)) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb4-23">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">e_area</span>(drawdown_abs, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">name =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Drawdown"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb4-24">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">e_tooltip</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">trigger =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"axis"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb4-25">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">e_theme</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"walden"</span>)</span>
<span id="cb4-26">    })</span>
<span id="cb4-27"></span>
<span id="cb4-28">    output<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>dd_stats <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">renderPrint</span>({</span>
<span id="cb4-29">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">drawdown_summary</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">dd_data</span>())</span>
<span id="cb4-30">    })</span>
<span id="cb4-31">  })</span>
<span id="cb4-32">}</span></code></pre></div>
</section>
<section id="detecting-regime-shifts" class="level2">
<h2 class="anchored" data-anchor-id="detecting-regime-shifts">Detecting Regime Shifts</h2>
<p>A strategy optimised on 2022-2023 EURUSD data may stop working when the Fed’s rate cycle shifts. Monitoring for regime change is the difference between a system that adapts and one that silently bleeds an account. A simple but effective detector uses a rolling Sharpe Ratio with a rolling window and flags when it crosses a threshold.</p>
<p>The regime logic in <code>app/logic/regime.R</code>:</p>
<div class="sourceCode" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb5-1">box<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">use</span>(dplyr[...], slider[slide_dbl], zoo[rollmean])</span>
<span id="cb5-2"></span>
<span id="cb5-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' Compute rolling Sharpe Ratio and flag regime shifts</span></span>
<span id="cb5-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#'</span></span>
<span id="cb5-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @param trades  A tibble from load_mt5_trades()</span></span>
<span id="cb5-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @param window  Rolling window in trades (default 50)</span></span>
<span id="cb5-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @param sr_warn Sharpe threshold below which a warning is flagged</span></span>
<span id="cb5-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @return A tibble with rolling Sharpe and a regime flag column</span></span>
<span id="cb5-9">detect_regime <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(trades, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">window =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">50</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">sr_warn =</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.5</span>) {</span>
<span id="cb5-10">  annualisation <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sqrt</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">252</span>)   <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Assumes roughly one trade per day on average</span></span>
<span id="cb5-11"></span>
<span id="cb5-12">  trades <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb5-13">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">arrange</span>(close_time) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb5-14">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mutate</span>(</span>
<span id="cb5-15">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">rolling_mean_pnl =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">slide_dbl</span>(profit, mean, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">.before =</span> window <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>,</span>
<span id="cb5-16">                                   <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">.complete =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>),</span>
<span id="cb5-17">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">rolling_sd_pnl   =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">slide_dbl</span>(profit, sd,   <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">.before =</span> window <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>,</span>
<span id="cb5-18">                                   <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">.complete =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>),</span>
<span id="cb5-19">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">rolling_sr       =</span> (rolling_mean_pnl <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> rolling_sd_pnl) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> annualisation,</span>
<span id="cb5-20">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">regime_warning   =</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">is.na</span>(rolling_sr) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&amp;</span> rolling_sr <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span> sr_warn</span>
<span id="cb5-21">    )</span>
<span id="cb5-22">}</span></code></pre></div>
<p>The <code>slider</code> package is well-suited here because it handles partial windows gracefully and integrates with the tidyverse pipe. When <code>regime_warning</code> flips to <code>TRUE</code>, the dashboard surfaces a red indicator in the overview tab. The threshold of 0.5 is conservative, consistent with the <a href="../../../blog/posts/deflated-sharpe-ratio/">Deflated Sharpe Ratio</a> reasoning that a Sharpe below a certain adjusted threshold signals the edge has degraded.</p>
</section>
<section id="deploying-without-an-institutional-budget" class="level2">
<h2 class="anchored" data-anchor-id="deploying-without-an-institutional-budget">Deploying Without an Institutional Budget</h2>
<p>The full Rhino app runs as a Docker container. A minimal <code>Dockerfile</code> for this dashboard:</p>
<div class="sourceCode" id="cb6" style="background: #f1f3f5;"><pre class="sourceCode dockerfile code-with-copy"><code class="sourceCode dockerfile"><span id="cb6-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">FROM</span> rocker/shiny-verse:4.4.0</span>
<span id="cb6-2"></span>
<span id="cb6-3"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">RUN</span> <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">apt-get</span> update <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">&amp;&amp;</span> <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">apt-get</span> install <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-y</span> libssl-dev libcurl4-openssl-dev</span>
<span id="cb6-4"></span>
<span id="cb6-5"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">WORKDIR</span> /app</span>
<span id="cb6-6"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">COPY</span> renv.lock renv.lock</span>
<span id="cb6-7"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">RUN</span> <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">R</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-e</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"renv::restore(lockfile = 'renv.lock', prompt = FALSE)"</span></span>
<span id="cb6-8"></span>
<span id="cb6-9"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">COPY</span> . .</span>
<span id="cb6-10"></span>
<span id="cb6-11"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">EXPOSE</span> 3838</span>
<span id="cb6-12"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">CMD</span> [<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"R"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"-e"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"rhino::app() |&gt; shiny::runApp(port = 3838, host = '0.0.0.0')"</span>]</span></code></pre></div>
<p>A DigitalOcean Droplet at $12/month (2 vCPU, 2 GB RAM) handles this comfortably for a single user or a small team monitoring shared strategies. Add Nginx in front for SSL termination and the total infrastructure cost stays well under $20/month, comparable to a single Bloomberg terminal keyboard and far below the cost of a missed drawdown.</p>
<p>For teams already running MT5 on a VPS, co-locating the dashboard on the same machine eliminates latency in trade log access. The <code>load_mt5_trades()</code> function accepts any file path, so it reads directly from a shared network folder or a scheduled <code>rsync</code> from the MT5 server. For a deeper look at hosting options for Shiny applications, see the <a href="../../../blog/posts/rshiny-hosting-guide/">R Shiny hosting guide</a>.</p>
</section>
<section id="where-the-real-edge-goes" class="level2">
<h2 class="anchored" data-anchor-id="where-the-real-edge-goes">Where the Real Edge Goes</h2>
<p>The traders who get consistent results from systematic strategies are not the ones with the best entry signals. They are the ones who know, in real time, whether their system is behaving within historical norms or has entered a new regime that their backtest never saw.</p>
<p>A Rhino-structured Shiny dashboard built the way described here gives a Kenyan quant trader the same observability infrastructure that quantitative hedge funds maintain at institutional scale: drawdown tracking, regime monitoring, and historical context, for the cost of a weekend’s work and a $12 VPS. The technology gap between a retail trader in Westlands and a prop desk in Sandton is far smaller than the monitoring gap.</p>
<p>The question worth sitting with: if your live performance diverged from your backtest six weeks ago, would your current tools tell you?</p>
<hr>
<p><em>Building systematic forex monitoring infrastructure? Kwiz Computing Technologies builds production R/Shiny applications for trading teams across East Africa. See how we structure <a href="../../../blog/posts/systematic-trading-r/">systematic trading in R</a> and how the <a href="../../../blog/posts/deflated-sharpe-ratio/">deflated Sharpe ratio</a> can sharpen your strategy validation before you touch live capital.</em></p>


</section>

 ]]></description>
  <category>enterprise-data-science</category>
  <category>R-Shiny</category>
  <guid>https://kwizresearch.com/blog/posts/kenyan-quant-traders-production-strategy-dashboards-r-shiny/</guid>
  <pubDate>Thu, 23 Apr 2026 00:00:00 GMT</pubDate>
  <media:content url="https://kwizresearch.com/blog/posts/kenyan-quant-traders-production-strategy-dashboards-r-shiny/thumbnail.png" medium="image" type="image/png"/>
</item>
<item>
  <title>EIA Reproducibility Under NEMA’s 2025 Regulations</title>
  <dc:creator>Kwiz Computing Technologies</dc:creator>
  <link>https://kwizresearch.com/blog/posts/reproducible-eia-reporting-nema-2025-quarto-r-workflow-kenya/</link>
  <description><![CDATA[ 




<p>NEMA’s gazetted Environmental (Impact Assessment and Audit) Regulations, 2025 introduced something Kenyan EIA practice has never formally required before: a mandatory digital submission format with an explicit audit trail obligation. If your firm still assembles EIA reports by copying figures from R or Excel into Word, that workflow is now a documented compliance gap, not merely an inefficiency.</p>
<p>This article walks through a concrete Quarto project structure that satisfies the 2025 digital submission requirements while cutting re-analysis time by 60-80% when NEMA reviewers request revised scenarios. The angle is different from our earlier post on <a href="../../../blog/posts/reproducible-eia-reporting-r-nema-kenya-2024/">targets pipelines for NEMA 2024 EMCA submissions</a>, and from our broader overview of <a href="../../../blog/posts/esia-reproducibility/">ESIA reproducibility principles</a>. Those posts focus on build-automation and reproducibility as best practice. This one addresses what the 2025 Regulations specifically require, and why Quarto’s multi-document project model maps directly onto those requirements.</p>
<section id="what-the-2025-regulations-actually-changed" class="level2">
<h2 class="anchored" data-anchor-id="what-the-2025-regulations-actually-changed">What the 2025 Regulations Actually Changed</h2>
<p>The Environmental (Impact Assessment and Audit) Regulations, 2025 introduced three provisions that directly affect how EIA reports are prepared, not just submitted.</p>
<p>First, Regulation 14(3) requires that digital submissions include a file manifest: a structured list of every document, dataset, and annex forming part of the submission, with version identifiers. A Word document exported to PDF with no machine-readable metadata does not satisfy this requirement.</p>
<p>Second, Regulation 22(1)(c) extends the audit trail requirement that previously applied only to Category C projects to all Category B and C reports. The audit trail must show how each figure and table in the main report relates to the underlying dataset. NEMA can request this trail during review or within 24 months of report approval.</p>
<p>Third, Regulation 18(5) specifies that revised reports submitted following NEMA queries must include a change log that identifies exactly which sections and data values changed, and why. A new PDF with a revised date in the header does not meet this standard.</p>
<p>These three requirements are precisely what a Quarto project with version control satisfies automatically. They are also precisely what a Word-plus-Excel workflow cannot satisfy without significant manual overhead.</p>
<p>Before the 2025 Regulations, the consequences of a numerically inconsistent EIA report were mainly reputational: a slow review, a query letter, some back-and-forth. Under the new framework, the consequences are structural.</p>
<p>A report that cannot produce the audit trail required by Regulation 22(1)(c) can be rejected on procedural grounds, separate from the technical merits. A change log that does not satisfy Regulation 18(5) means a revised submission is treated as a new application, resetting the statutory review timeline. These outcomes affect project financing timelines and client relationships in ways that the previous informal review process did not.</p>
<p>The specific failure mode in copy-paste workflows is well-documented. A PM2.5 baseline value computed in Excel gets pasted into the executive summary. The data is later corrected during field verification. The analyst updates the technical annex but forgets the executive summary. NEMA receives a report where the same metric reads differently in two places. Under the 2025 audit trail requirement, explaining this discrepancy requires showing the computation history, which no Excel file preserves by default.</p>
</section>
<section id="structuring-an-eia-as-a-quarto-website-project" class="level2">
<h2 class="anchored" data-anchor-id="structuring-an-eia-as-a-quarto-website-project">Structuring an EIA as a Quarto Website Project</h2>
<p>Quarto supports two distinct output modes: a single document (one <code>.qmd</code> file rendered to one PDF) and a project (multiple <code>.qmd</code> files rendered as a coordinated set). For NEMA 2025 compliance, the project mode is the right choice. It maps one <code>.qmd</code> file per EIA chapter, produces a file manifest automatically, and makes the change log between versions machine-readable through Git.</p>
<p>A Quarto EIA project uses a <code>_quarto.yml</code> file at the root to define the submission structure:</p>
<div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode yaml code-with-copy"><code class="sourceCode yaml"><span id="cb1-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">project</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb1-2"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> book</span></span>
<span id="cb1-3"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">output-dir</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> _submission</span></span>
<span id="cb1-4"></span>
<span id="cb1-5"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">book</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb1-6"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">title</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Environmental Impact Assessment"</span></span>
<span id="cb1-7"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">subtitle</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"NEMA Reference: ?meta:nema_ref"</span></span>
<span id="cb1-8"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">author</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Kwiz Computing Technologies"</span></span>
<span id="cb1-9"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">date</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> today</span></span>
<span id="cb1-10"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chapters</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb1-11"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> index.qmd</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">                        # Executive Summary</span></span>
<span id="cb1-12"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> chapters/01-project-description.qmd</span></span>
<span id="cb1-13"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> chapters/02-legal-framework.qmd</span></span>
<span id="cb1-14"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> chapters/03-baseline-environment.qmd</span></span>
<span id="cb1-15"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> chapters/04-impact-assessment.qmd</span></span>
<span id="cb1-16"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> chapters/05-mitigation-measures.qmd</span></span>
<span id="cb1-17"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> chapters/06-alternatives-analysis.qmd</span></span>
<span id="cb1-18"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> chapters/07-monitoring-plan.qmd</span></span>
<span id="cb1-19"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> references.qmd</span></span>
<span id="cb1-20"></span>
<span id="cb1-21"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">format</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb1-22"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">pdf</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb1-23"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">toc</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">true</span></span>
<span id="cb1-24"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">number-sections</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">true</span></span>
<span id="cb1-25"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">lof</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">true</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">                          # List of figures</span></span>
<span id="cb1-26"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">lot</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">true</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">                          # List of tables</span></span>
<span id="cb1-27"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">keep-tex</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">false</span></span>
<span id="cb1-28"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">html</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb1-29"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">theme</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> flatly</span></span>
<span id="cb1-30"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">self-contained</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">false</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">              # Produces the file manifest NEMA requires</span></span>
<span id="cb1-31"></span>
<span id="cb1-32"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">bibliography</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> references.bib</span></span></code></pre></div>
<p>The HTML output is not decorative here. Rendering to HTML produces a <code>site_libs/</code> directory and an <code>index.html</code> with linked chapter files, which constitutes the machine-readable file manifest that Regulation 14(3) requires. You submit the HTML output alongside the PDF as the digital package.</p>
<p>The project directory structure designed for NEMA audit compliance looks like this:</p>
<pre><code>project-nema-ref-2026-001/
├── _quarto.yml                        # Submission structure definition
├── params.yml                         # Project-wide parameters (see below)
├── index.qmd                          # Executive Summary
├── chapters/
│   ├── 01-project-description.qmd
│   ├── 02-legal-framework.qmd
│   ├── 03-baseline-environment.qmd
│   ├── 04-impact-assessment.qmd
│   ├── 05-mitigation-measures.qmd
│   ├── 06-alternatives-analysis.qmd
│   └── 07-monitoring-plan.qmd
├── R/
│   ├── baseline_air.R                 # Functions, not scripts
│   ├── baseline_water.R
│   ├── baseline_biodiversity.R
│   └── spatial_analysis.R
├── data/
│   ├── raw/                           # Field data, never modified
│   └── processed/                     # Outputs from R functions
├── _submission/                       # Rendered output (git-ignored)
├── CHANGELOG.md                       # Regulation 18(5) change log
├── MANIFEST.md                        # Regulation 14(3) file manifest
└── renv.lock                          # Exact R package versions</code></pre>
<p>The <code>CHANGELOG.md</code> file is not documentation overhead. It is the Regulation 18(5) artefact. Every time you render a revised submission, you update it. Git then records when that update happened and who made it, satisfying the audit trail requirement automatically.</p>
</section>
<section id="parameterised-chapters-for-scenario-comparison" class="level2">
<h2 class="anchored" data-anchor-id="parameterised-chapters-for-scenario-comparison">Parameterised Chapters for Scenario Comparison</h2>
<p>The 2025 Regulations require that alternatives analyses compare at minimum: the proposed project, the no-project alternative, and one modified-design alternative. These three scenarios typically share the same baseline data but differ in their spatial footprint, mitigation assumptions, and predicted impact magnitudes.</p>
<p>In a copy-paste workflow, maintaining three internally consistent versions of Chapter 6 is genuinely difficult. A Quarto parameterised chapter makes it trivial. Each scenario is a single parameter value; the chapter code handles the rest.</p>
<p>A <code>params.yml</code> file at the project root defines the project-wide parameters:</p>
<div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode yaml code-with-copy"><code class="sourceCode yaml"><span id="cb3-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># params.yml</span></span>
<span id="cb3-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">project_name</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Thika Road Industrial Park Phase 3"</span></span>
<span id="cb3-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">nema_ref</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"NEMA/EIA/5/2/2026/042"</span></span>
<span id="cb3-4"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">county</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Kiambu"</span></span>
<span id="cb3-5"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">scenario</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"proposed"</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">          # One of: proposed, no-project, alternative-1</span></span>
<span id="cb3-6"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">baseline_year</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2025</span></span>
<span id="cb3-7"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">review_round</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span></span></code></pre></div>
<p>Inside <code>chapters/04-impact-assessment.qmd</code>, the scenario parameter drives which analysis branch runs:</p>
<div class="sourceCode" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb4-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#| label: setup</span></span>
<span id="cb4-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#| include: false</span></span>
<span id="cb4-3"></span>
<span id="cb4-4"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(dplyr)</span>
<span id="cb4-5"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(sf)</span>
<span id="cb4-6"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(ggplot2)</span>
<span id="cb4-7"></span>
<span id="cb4-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Read parameters from the YAML</span></span>
<span id="cb4-9">scenario    <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> params<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>scenario</span>
<span id="cb4-10">nema_ref    <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> params<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>nema_ref</span>
<span id="cb4-11">county      <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> params<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>county</span>
<span id="cb4-12"></span>
<span id="cb4-13"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Load pre-processed baseline (shared across all scenarios)</span></span>
<span id="cb4-14">baseline_air <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">readRDS</span>(here<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">here</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"data/processed/baseline_air.rds"</span>))</span>
<span id="cb4-15"></span>
<span id="cb4-16"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Load scenario-specific impact predictions</span></span>
<span id="cb4-17">impact_data <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">readRDS</span>(</span>
<span id="cb4-18">  here<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">here</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">paste0</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"data/processed/impacts_"</span>, scenario, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">".rds"</span>))</span>
<span id="cb4-19">)</span></code></pre></div>
<p>To render all three scenario variants in one pass, a small R script calls <code>quarto_render()</code> three times with different parameter overrides:</p>
<div class="sourceCode" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb5-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># render_scenarios.R</span></span>
<span id="cb5-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(quarto)</span>
<span id="cb5-3"></span>
<span id="cb5-4">scenarios <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"proposed"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"no-project"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"alternative-1"</span>)</span>
<span id="cb5-5"></span>
<span id="cb5-6"><span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">for</span> (s <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">in</span> scenarios) {</span>
<span id="cb5-7">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">quarto_render</span>(</span>
<span id="cb5-8">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">input       =</span> here<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">here</span>(),</span>
<span id="cb5-9">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">output_file =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">paste0</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"submission_"</span>, s, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">".pdf"</span>),</span>
<span id="cb5-10">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">execute_params =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">scenario =</span> s)</span>
<span id="cb5-11">  )</span>
<span id="cb5-12">}</span></code></pre></div>
<p>Running <code>Rscript render_scenarios.R</code> from the project root produces three complete, internally consistent PDF reports. Every table and figure in each report is generated from the same R functions applied to scenario-specific input data. There is no opportunity for a figure in Chapter 4 to depict a different scenario than the table in Chapter 6.</p>
</section>
<section id="building-the-nema-readable-audit-trail-with-git" class="level2">
<h2 class="anchored" data-anchor-id="building-the-nema-readable-audit-trail-with-git">Building the NEMA-Readable Audit Trail with Git</h2>
<p>Git is the audit trail mechanism that satisfies Regulation 22(1)(c) for baseline data and analytical decisions. The key principle is that commits map to analytical events, not to working sessions.</p>
<p>Use a simple commit convention that mirrors the EIA chapter structure:</p>
<pre><code>data: add Q4 2025 air quality monitoring, Thika Road stations 1-4
analysis: update NOx baseline calculation per NEMA guidance note 2025-03
report: revise Section 4.2 following NEMA Round 1 query RQ-2026-042-001
params: change review_round from 1 to 2 for revised submission</code></pre>
<p>When a NEMA reviewer asks how the PM2.5 baseline value in Table 4.1 changed between your draft submission and your revised submission, the answer is a single command:</p>
<div class="sourceCode" id="cb7" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb7-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">git</span> log <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">--oneline</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">--</span> data/raw/baseline_air_quality.csv</span>
<span id="cb7-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">git</span> diff v1.0..v2.0 <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">--</span> chapters/03-baseline-environment.qmd</span></code></pre></div>
<p>The first command shows every commit that touched the source data file. The second shows exactly which lines in the baseline chapter changed between submission versions. This is a complete audit trail under Regulation 22(1)(c), and it takes seconds to produce.</p>
<p>Tag each formal submission to NEMA as a Git release:</p>
<div class="sourceCode" id="cb8" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb8-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">git</span> tag <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-a</span> v1.0 <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-m</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Initial NEMA submission, </span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">$(</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">date</span> +%Y-%m-%d<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">)</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span></span>
<span id="cb8-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">git</span> tag <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-a</span> v2.0 <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-m</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Revised submission following NEMA Round 1 queries"</span></span></code></pre></div>
<p>The tag history becomes the submission history. If NEMA requests the analysis state at the time of the original submission, you check out <code>v1.0</code> and re-render. The report is bit-for-bit reproducible because <code>renv.lock</code> pins the exact R package versions that produced it.</p>
</section>
<section id="the-workflow-when-nema-requests-revised-figures" class="level2">
<h2 class="anchored" data-anchor-id="the-workflow-when-nema-requests-revised-figures">The Workflow When NEMA Requests Revised Figures</h2>
<p>This is where the 60-80% time saving materialises. A typical NEMA Round 1 query on a baseline-heavy EIA asks for: revised noise contours using a corrected receptor point, updated tables with a new statistical method, and a revised alternatives comparison reflecting the modified footprint.</p>
<p>In a copy-paste workflow, each of these is a multi-day task: locate the relevant spreadsheet, recompute, re-export the figure, reformat it to match the report template, paste it into Word, recheck cross-references, regenerate the PDF. For three queries simultaneously, this takes a week or more, and introduces new inconsistency risks.</p>
<p>In the Quarto project workflow:</p>
<ol type="1">
<li>Update the relevant R function in <code>R/</code> or the input data file in <code>data/raw/</code>.</li>
<li>Commit the change with a message referencing the NEMA query number.</li>
<li>Update <code>params.yml</code> to increment <code>review_round</code>.</li>
<li>Add an entry to <code>CHANGELOG.md</code> describing the revision.</li>
<li>Run <code>quarto render</code> from the project root.</li>
</ol>
<p>Step 5 regenerates every chapter, every figure, and every table in the report. Cross-references update automatically. The list of figures in the PDF front matter reflects the current figures. The HTML output updates the file manifest. The entire revised submission is ready in the time it takes Quarto to render, typically three to eight minutes for a full EIA report with spatial figures.</p>
<p>The commit history then automatically documents which query prompted which change, satisfying Regulation 18(5) without any additional record-keeping. See our earlier post on <a href="../../../blog/posts/gbif-kenya-data-quality/">GBIF Kenya data quality</a> for how this same principle applies to biodiversity baseline data preparation, which is often the most query-prone section of a Category B EIA.</p>
</section>
<section id="where-to-begin" class="level2">
<h2 class="anchored" data-anchor-id="where-to-begin">Where to Begin</h2>
<p>The <a href="../../../blog/posts/kenya-open-data/">Kenya open data portal</a> and NEMA’s own public database provide reference data that belongs in the <code>data/raw/</code> layer of this project structure. Baseline meteorological data from the Kenya Meteorological Department, national ambient air quality standards from EMCA’s subsidiary legislation, and county-level land use data from the Survey of Kenya all carry stable citation sources that can be documented in <code>MANIFEST.md</code>. Citing a programmatically fetched dataset with a recorded download date and file hash satisfies NEMA’s documentation requirements. A ZIP file someone downloaded to a personal laptop does not.</p>
<p>If your firm has a NEMA submission due in the next quarter, the fastest entry point is not a full migration. Start with one chapter, the baseline environment assessment (Chapter 3 or equivalent), and structure it as a Quarto document that reads from a single R script. Render it alongside your existing Word document, verify the outputs match, then add the Git repository and commit the first working version.</p>
<p>The Quarto documentation at <code>quarto.org/docs/books/</code> covers the multi-chapter project format. The <code>renv</code> package documentation at <code>rstudio.github.io/renv/</code> covers environment reproducibility. Both are prerequisites for a submission that can satisfy Regulation 22(1)(c) when NEMA requests the audit trail.</p>
<p>The 2025 Regulations did not make reproducible workflows optional for Kenyan EIA practitioners. They made the cost of not having one explicit and specific. The question for licensed EIA lead experts is not whether to adopt a structured workflow, but which project gets the first compliant submission.</p>
<hr>
<p><em>Kwiz Computing Technologies provides environmental data science services for EIA and ESIA projects across East Africa. If your consultancy is building a Quarto-based reporting workflow for NEMA 2025 compliance, <a href="https://kwizresearch.com/contact">contact us</a> to discuss your project structure.</em></p>


</section>

 ]]></description>
  <category>environmental-analytics</category>
  <category>EIA</category>
  <guid>https://kwizresearch.com/blog/posts/reproducible-eia-reporting-nema-2025-quarto-r-workflow-kenya/</guid>
  <pubDate>Thu, 23 Apr 2026 00:00:00 GMT</pubDate>
  <media:content url="https://kwizresearch.com/blog/posts/reproducible-eia-reporting-nema-2025-quarto-r-workflow-kenya/thumbnail.png" medium="image" type="image/png"/>
</item>
<item>
  <title>African Retail Forex: The Quant Gap Data Science Can Fix</title>
  <dc:creator>Kwiz Computing Technologies</dc:creator>
  <link>https://kwizresearch.com/blog/posts/quant-gap-african-retail-traders-vs-institutional-algorithms-data-science/</link>
  <description><![CDATA[ 




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


</section>

 ]]></description>
  <category>Quantitative Finance</category>
  <category>Kwiz Quants</category>
  <category>thought-leadership</category>
  <guid>https://kwizresearch.com/blog/posts/quant-gap-african-retail-traders-vs-institutional-algorithms-data-science/</guid>
  <pubDate>Thu, 23 Apr 2026 00:00:00 GMT</pubDate>
  <media:content url="https://kwizresearch.com/blog/posts/quant-gap-african-retail-traders-vs-institutional-algorithms-data-science/thumbnail.png" medium="image" type="image/png"/>
</item>
<item>
  <title>NEMA EIA Report in R: 2024 EMCA Reproducibility Guide</title>
  <dc:creator>Kwiz Computing Technologies</dc:creator>
  <link>https://kwizresearch.com/blog/posts/reproducible-eia-reporting-r-nema-kenya-2024/</link>
  <description><![CDATA[ 




<p>A Category C EIA submitted for a road project along the Nairobi-Mombasa Economic Corridor comes back from NEMA in Q1 2025 with a query: the noise contour table in Section 4.3 does not match the figure in Annex B, and the baseline PM2.5 values differ by 12% between the executive summary and the technical appendix. The consultancy has six weeks to respond. Nobody can trace which version of the spreadsheet produced which number.</p>
<p>This is not a hypothetical. Several high-profile infrastructure EIAs were returned by NEMA in early 2025 for exactly these reasons, and the 2024 EMCA amendment has made the stakes higher: Category B and C reports now go through a mandatory peer-review stage where a licensed EIA Lead Expert can formally challenge any numerical claim in the document. Reproducibility has stopped being best practice. It is now a defence against report rejection.</p>
<section id="why-nemas-2024-amendment-changes-the-rules" class="level2">
<h2 class="anchored" data-anchor-id="why-nemas-2024-amendment-changes-the-rules">Why NEMA’s 2024 Amendment Changes the Rules</h2>
<p>Before the amendment, most NEMA review queries were handled informally between a firm and the review officer. The 2024 Environmental Management and Co-ordination Act amendment introduced a structured peer-review stage that puts a second qualified expert in the room, one with the authority to raise formal objections that delay approval.</p>
<p>The practical consequence is that any table, figure, or statistic in your report needs a clear chain of custody from raw data to final output. If your baseline water quality data lives in one Excel file, your calculations live in another, and your report was written by copying numbers into Word, you have no chain of custody. You have a liability.</p>
<p>Structured as a version-controlled R project with a <code>{targets}</code> pipeline and parameterised Quarto output, the same report becomes self-auditing: every number traces back to a function, every function traces back to a data file, and every data file carries a hash that confirms it has not been altered since the analysis ran.</p>
</section>
<section id="the-targets-package-what-it-does-for-eia-work" class="level2">
<h2 class="anchored" data-anchor-id="the-targets-package-what-it-does-for-eia-work">The {targets} Package: What It Does for EIA Work</h2>
<p>The <code>{targets}</code> package by Will Landau implements pipeline-based computation for R. Instead of running scripts sequentially, you declare a set of targets, each one a named object that depends on other targets or on raw inputs. <code>{targets}</code> builds only what has changed since the last run. It records the state of every intermediate object. It skips targets that are already up to date.</p>
<p>For an EIA project, this maps directly to the structure of the work: baseline data collection feeds into data cleaning, which feeds into statistical analysis, which feeds into the tables and figures that populate the report.</p>
<p>A minimal <code>_targets.R</code> file for a Category B EIA looks like this:</p>
<div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb1-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(targets)</span>
<span id="cb1-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(tarchetypes)</span>
<span id="cb1-3"></span>
<span id="cb1-4"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tar_option_set</span>(</span>
<span id="cb1-5">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">packages =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"dplyr"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"readxl"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"sf"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"ggplot2"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"quarto"</span>)</span>
<span id="cb1-6">)</span>
<span id="cb1-7"></span>
<span id="cb1-8"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(</span>
<span id="cb1-9">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 1. Raw data ingestion</span></span>
<span id="cb1-10">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tar_target</span>(</span>
<span id="cb1-11">    raw_air_quality,</span>
<span id="cb1-12">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">read_excel</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"data/raw/baseline_air_quality.xlsx"</span>),</span>
<span id="cb1-13">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">format =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"rds"</span></span>
<span id="cb1-14">  ),</span>
<span id="cb1-15">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tar_target</span>(</span>
<span id="cb1-16">    raw_water_quality,</span>
<span id="cb1-17">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">read_excel</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"data/raw/baseline_water_quality.xlsx"</span>),</span>
<span id="cb1-18">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">format =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"rds"</span></span>
<span id="cb1-19">  ),</span>
<span id="cb1-20"></span>
<span id="cb1-21">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 2. Cleaning and validation</span></span>
<span id="cb1-22">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tar_target</span>(</span>
<span id="cb1-23">    air_quality_clean,</span>
<span id="cb1-24">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">clean_air_quality</span>(raw_air_quality),</span>
<span id="cb1-25">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">format =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"rds"</span></span>
<span id="cb1-26">  ),</span>
<span id="cb1-27">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tar_target</span>(</span>
<span id="cb1-28">    water_quality_clean,</span>
<span id="cb1-29">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">clean_water_quality</span>(raw_water_quality),</span>
<span id="cb1-30">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">format =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"rds"</span></span>
<span id="cb1-31">  ),</span>
<span id="cb1-32"></span>
<span id="cb1-33">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 3. Statistical summaries</span></span>
<span id="cb1-34">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tar_target</span>(</span>
<span id="cb1-35">    air_summary_table,</span>
<span id="cb1-36">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">summarise_air_baseline</span>(air_quality_clean),</span>
<span id="cb1-37">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">format =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"rds"</span></span>
<span id="cb1-38">  ),</span>
<span id="cb1-39">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tar_target</span>(</span>
<span id="cb1-40">    water_summary_table,</span>
<span id="cb1-41">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">summarise_water_baseline</span>(water_quality_clean),</span>
<span id="cb1-42">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">format =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"rds"</span></span>
<span id="cb1-43">  ),</span>
<span id="cb1-44"></span>
<span id="cb1-45">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 4. Parameterised report</span></span>
<span id="cb1-46">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tar_quarto</span>(</span>
<span id="cb1-47">    eia_report,</span>
<span id="cb1-48">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">path =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"report/eia_report.qmd"</span>,</span>
<span id="cb1-49">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">execute_params =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(</span>
<span id="cb1-50">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">project_name =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Nairobi Southern Bypass Phase 2"</span>,</span>
<span id="cb1-51">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">nema_ref =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"NEMA/EIA/5/2/2026/001"</span>,</span>
<span id="cb1-52">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">reporting_period =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Q4 2025"</span></span>
<span id="cb1-53">    )</span>
<span id="cb1-54">  )</span>
<span id="cb1-55">)</span></code></pre></div>
<p>Running <code>tar_make()</code> in the project root builds the entire pipeline. Running it again after changing a single data file rebuilds only the targets that depend on that file, and nothing else. Every intermediate result is stored with a content hash. If someone changes <code>baseline_air_quality.xlsx</code> and does not tell you, the next <code>tar_make()</code> will detect the change and recompute downstream targets automatically.</p>
</section>
<section id="parameterised-quarto-reports" class="level2">
<h2 class="anchored" data-anchor-id="parameterised-quarto-reports">Parameterised Quarto Reports</h2>
<p>The report itself is a Quarto document with a <code>params</code> block in its YAML header. Parameters are injected at render time by <code>{targets}</code>, so the same <code>.qmd</code> template can produce outputs for different project phases, project areas, or reporting periods without any manual editing.</p>
<p>A simplified <code>eia_report.qmd</code> header:</p>
<div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode yaml code-with-copy"><code class="sourceCode yaml"><span id="cb2-1"><span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">---</span></span>
<span id="cb2-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">title</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Environmental Impact Assessment: `r params$project_name`"</span></span>
<span id="cb2-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">subtitle</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"NEMA Reference: `r params$nema_ref`"</span></span>
<span id="cb2-4"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">author</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Kwiz Computing Technologies"</span></span>
<span id="cb2-5"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">date</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"`r Sys.Date()`"</span></span>
<span id="cb2-6"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">format</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb2-7"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">pdf</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb2-8"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">toc</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">true</span></span>
<span id="cb2-9"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">number-sections</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">true</span></span>
<span id="cb2-10"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">keep-tex</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">false</span></span>
<span id="cb2-11"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">params</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb2-12"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">project_name</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Default Project"</span></span>
<span id="cb2-13"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">nema_ref</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"NEMA/EIA/5/2/XXXX/XXX"</span></span>
<span id="cb2-14"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">reporting_period</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Q1 2026"</span></span>
<span id="cb2-15"><span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">---</span></span></code></pre></div>
<p>Inside the document, every table and figure calls a target object directly:</p>
<div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb3-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># In a code chunk: pull the pre-computed summary table</span></span>
<span id="cb3-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tar_read</span>(air_summary_table) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb3-3">  knitr<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">kable</span>(</span>
<span id="cb3-4">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">caption =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">paste0</span>(</span>
<span id="cb3-5">      <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Table 4.1: Baseline Air Quality Summary, "</span>,</span>
<span id="cb3-6">      params<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>reporting_period</span>
<span id="cb3-7">    ),</span>
<span id="cb3-8">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">digits =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span></span>
<span id="cb3-9">  )</span></code></pre></div>
<p>Because <code>tar_read()</code> retrieves the stored target object rather than recomputing it, every figure and table in the rendered PDF is guaranteed to match the pipeline output exactly. The discrepancy that triggers NEMA queries, a number in the executive summary that differs from the technical appendix, cannot occur when both sections pull from the same target.</p>
<section id="project-structure-for-nema-submissions" class="level3">
<h3 class="anchored" data-anchor-id="project-structure-for-nema-submissions">Project Structure for NEMA Submissions</h3>
<p>A reproducible EIA project follows a standard directory layout that any colleague or reviewer can read and understand immediately:</p>
<pre><code>project-nema-eia-001/
├── _targets.R              # Pipeline definition
├── _targets/               # Cached pipeline state (git-ignored)
├── R/
│   ├── clean_air_quality.R
│   ├── clean_water_quality.R
│   ├── summarise_air_baseline.R
│   └── summarise_water_baseline.R
├── data/
│   ├── raw/                # Original field data (never modified)
│   └── processed/          # Outputs written by the pipeline
├── report/
│   ├── eia_report.qmd      # Main report template
│   └── sections/           # Child documents for each chapter
├── output/                 # Rendered PDF reports
├── renv.lock               # Exact package versions (reproducible environment)
└── README.md               # Instructions to reproduce the analysis</code></pre>
<p>The <code>data/raw/</code> directory holds only original files, never overwritten by the pipeline. The <code>R/</code> directory holds plain functions, one per file, each testable in isolation. The <code>renv.lock</code> file captures the exact version of every R package used, so the analysis runs the same way on a colleague’s machine six months later.</p>
</section>
<section id="version-control-for-eia-audit-trails" class="level3">
<h3 class="anchored" data-anchor-id="version-control-for-eia-audit-trails">Version Control for EIA Audit Trails</h3>
<p>Git provides the audit trail that NEMA’s peer-review stage now implicitly requires. Each commit records who changed what and when, giving you a documented history of every analytical decision.</p>
<p>For EIA work, adopt a simple commit convention:</p>
<ul>
<li><code>data: add Q3 2025 air quality monitoring from Athi River station</code></li>
<li><code>analysis: update PM2.5 baseline method per NEMA guidance note 2024-07</code></li>
<li><code>report: revise Section 4.3 noise assessment following peer-review comments</code></li>
</ul>
<p>This history answers the most common NEMA query, “How did this number change between the draft and final report?”, in seconds. You check out the relevant commit and re-render.</p>
<p>Store your <code>_targets/</code> cache directory in <code>.gitignore</code> because it is large and reconstructable. Store your <code>data/raw/</code> files in the repository if they are small enough, or document exactly where they come from and how to obtain them if they are large field datasets.</p>
<p>A note on sensitive project data: consult your firm’s data governance policy before committing client data to any repository. Many Kenyan consultancies use private GitHub or GitLab repositories hosted under their organisation account, which is adequate for EMCA compliance purposes.</p>
</section>
</section>
<section id="migrating-from-excel-based-workflows" class="level2">
<h2 class="anchored" data-anchor-id="migrating-from-excel-based-workflows">Migrating from Excel-Based Workflows</h2>
<p>Most Kenyan EIA consultancies do not start with a clean slate. They have existing Excel-based workflows, established templates, and staff who are not R users. The migration does not have to be immediate or total.</p>
<p>A practical three-phase approach:</p>
<p><strong>Phase 1: Read, do not replace.</strong> Keep your Excel files as the primary data store. Write R functions that read from them using <code>readxl</code> or <code>openxlsx</code>. Run those functions through a <code>{targets}</code> pipeline. The team keeps working in Excel; R becomes the computation layer downstream of it.</p>
<p><strong>Phase 2: Parameterise the report.</strong> Move one chapter of your standard NEMA report template into a Quarto document. Have it read from the <code>{targets}</code> pipeline. Render it alongside the Word document initially. Once the team trusts the output, the Quarto version becomes the primary.</p>
<p><strong>Phase 3: Bring data entry into R.</strong> Once the pipeline is established, replace manual Excel data entry with validated R data entry forms or structured CSV templates. At this stage the full pipeline is reproducible from raw inputs.</p>
<p>The two packages that smooth this transition most are <code>readxl</code> for reading Excel files without converting them, and <code>writexl</code> for producing Excel outputs if a client or regulator specifically requires them. Neither requires Excel to be installed.</p>
<p>For biodiversity baseline data, our earlier post on <a href="../../../blog/posts/gbif-kenya-data-quality/">GBIF Kenya data quality</a> covers the specific quality checks that belong in the cleaning stage of your pipeline.</p>
</section>
<section id="what-nema-peer-reviewers-actually-look-for" class="level2">
<h2 class="anchored" data-anchor-id="what-nema-peer-reviewers-actually-look-for">What NEMA Peer Reviewers Actually Look For</h2>
<p>Conversations with practitioners who have been through the 2024 peer-review process point to three recurring flashpoints.</p>
<p>First, inconsistent summary statistics: a mean value reported differently in the executive summary, the baseline chapter, and the annexes. This is a copy-paste artefact that a <code>{targets}</code> pipeline eliminates entirely because every section pulls from the same computed object.</p>
<p>Second, undocumented data transformations: a baseline dataset that has been filtered or corrected without any record of the decision. The <code>R/</code> functions in your project document every transformation in code, and Git records when and why the code changed.</p>
<p>Third, outdated figures: a map or chart that reflects an earlier version of the data because the analyst forgot to regenerate it after a late field correction. <code>{targets}</code> makes stale outputs structurally impossible: if the data changes, the pipeline marks downstream targets as outdated and rebuilds them on the next <code>tar_make()</code> call.</p>
<p>The <a href="../../../blog/posts/kenya-open-data/">Kenya open data portal</a> and the previous post on <a href="../../../blog/posts/esia-reproducibility/">ESIA reproducibility</a> provide additional context on the data sources and principles that underpin this kind of workflow.</p>
<section id="where-to-start" class="level3">
<h3 class="anchored" data-anchor-id="where-to-start">Where to Start</h3>
<p>If you are preparing a Category B or C EIA and want to move toward a reproducible workflow before your next NEMA submission, three resources are worth your time: the <code>{targets}</code> user manual at <code>books.ropensci.org/targets</code>, the <code>renv</code> documentation for package environment management, and Quarto’s guide to parameterised reports at <code>quarto.org/docs/computations/parameters.html</code>.</p>
<p>The change is not primarily technical. The shift from an Excel-based workflow to a <code>{targets}</code> pipeline takes an experienced R practitioner one to two weeks for a typical Category B project. The harder change is procedural: agreeing within your consultancy that the pipeline, not the spreadsheet, is the authoritative version of the analysis.</p>
<p>Given that NEMA’s peer-review stage now means a qualified external expert can formally challenge any number in your report, that agreement has real commercial value. A rejected report costs more than a reproducible workflow.</p>
<hr>
<p><em>Kwiz Computing Technologies provides environmental data science services for EIA and ESIA projects across East Africa. If your consultancy is scoping a move to reproducible R workflows for NEMA submissions, <a href="https://kwizresearch.com/contact">contact us</a> to discuss your project.</em></p>


</section>
</section>

 ]]></description>
  <category>environmental-analytics</category>
  <category>EIA</category>
  <guid>https://kwizresearch.com/blog/posts/reproducible-eia-reporting-r-nema-kenya-2024/</guid>
  <pubDate>Thu, 23 Apr 2026 00:00:00 GMT</pubDate>
  <media:content url="https://kwizresearch.com/blog/posts/reproducible-eia-reporting-r-nema-kenya-2024/thumbnail.png" medium="image" type="image/png"/>
</item>
<item>
  <title>Systematic Trading R: Institutional Grade at Near-Zero Cost</title>
  <dc:creator>Kwiz Computing Technologies</dc:creator>
  <link>https://kwizresearch.com/blog/posts/kenyan-quants-institutional-trading-systems-r-shiny-rhino/</link>
  <description><![CDATA[ 




<section id="the-25000-problem" class="level2">
<h2 class="anchored" data-anchor-id="the-25000-problem">The $25,000 Problem</h2>
<p>A Bloomberg Terminal subscription costs roughly $25,000 per year. A FactSet seat runs between $12,000 and $24,000 annually. A proprietary prop desk at Stanbic or CBA running systematic equity strategies likely layers on top of that: execution management systems, prime brokerage data feeds, and custom risk servers that together represent hundreds of thousands of dollars in annual spend.</p>
<p>All four functional components those systems deliver (a data layer, a backtesting engine, a signal dashboard, and a live risk monitor) can be built in R, structured with the Rhino framework, and deployed on a $24/month VPS. This is not a compromise. It is the same architecture, without the licensing overhead that makes institutional infrastructure inaccessible to independent Kenyan quants.</p>
</section>
<section id="why-this-matters-in-kenya-right-now" class="level2">
<h2 class="anchored" data-anchor-id="why-this-matters-in-kenya-right-now">Why This Matters in Kenya Right Now</h2>
<p>The Nairobi Securities Exchange retail participation rate has grown, but the tools available to individual traders have not kept pace with the sophistication of those traders. Kenya’s forex community is one of the most active in Sub-Saharan Africa, with thousands of retail participants running algorithmic strategies on MT5. Many have genuine quantitative skills: statistics degrees, actuarial backgrounds, engineering training.</p>
<p>The gap is not talent. The gap is infrastructure. A quant at a Nairobi prop desk has a Bloomberg terminal, a risk system, and a data warehouse. An equally skilled independent quant in Westlands has a laptop and a spreadsheet.</p>
<p>R closes that gap faster than any other tool stack. The packages exist, the deployment patterns are proven, and the R/Shiny skill set that Nairobi’s data consultancy community already uses for enterprise dashboards translates directly to trading infrastructure. The same Rhino-structured Shiny application that powers a client-facing business intelligence dashboard can, with different modules, run a live signal monitor for a systematic forex strategy.</p>
</section>
<section id="the-four-components-of-a-complete-trading-system" class="level2">
<h2 class="anchored" data-anchor-id="the-four-components-of-a-complete-trading-system">The Four Components of a Complete Trading System</h2>
<p>Institutional trading infrastructure, stripped to its functional core, has four layers. Understanding what each layer does makes it clear why R covers all four without reaching for expensive commercial alternatives.</p>
<p><strong>The data layer</strong> ingests price data, cleans it, stores it, and makes it queryable. For NSE equities, this means pulling end-of-day prices. For forex, it means connecting to a broker’s historical tick or OHLCV data. For macro signals, it means scraping Central Bank of Kenya rate decisions or pulling World Bank API data.</p>
<p><strong>The backtesting engine</strong> applies a strategy to historical data to estimate how it would have performed. It handles position sizing, transaction cost modelling, and produces performance metrics. A proper backtesting engine avoids look-ahead bias and accounts for the statistical problems that make most backtests unreliable.</p>
<p><strong>The signal dashboard</strong> displays current live signals from the strategy, the current position or recommended position, and enough context to understand why the signal fired. This is the component a trader watches during market hours.</p>
<p><strong>The risk monitor</strong> tracks live exposure, drawdown from peak, value at risk, and any circuit-breaker conditions that should force a position reduction or halt. It runs continuously and alerts when thresholds are breached.</p>
<p>These four components map cleanly onto a single Rhino project.</p>
</section>
<section id="rhino-project-structure-for-a-full-trading-system" class="level2">
<h2 class="anchored" data-anchor-id="rhino-project-structure-for-a-full-trading-system">Rhino Project Structure for a Full Trading System</h2>
<p>Rhino is Appsilon’s framework for building production-grade Shiny applications with engineering discipline. It enforces a module system, separates logic from presentation, and integrates testing from the start. For a trading system, this separation matters enormously: backtesting logic, signal computation, and risk calculations should be testable pure R functions, independent of any Shiny dependency.</p>
<p>A complete Rhino trading system project looks like this:</p>
<pre><code>kwiz-trading-system/
├── app/
│   ├── main.R                    # Entry point, tab routing
│   ├── logic/
│   │   ├── data/
│   │   │   ├── nse_prices.R      # NSE equity price ingestion
│   │   │   ├── forex_ohlcv.R     # Forex OHLCV from broker API
│   │   │   └── macro_signals.R   # CBK rates, macro data
│   │   ├── backtest/
│   │   │   ├── engine.R          # Vectorised backtest runner
│   │   │   ├── metrics.R         # Sharpe, drawdown, Calmar
│   │   │   └── walk_forward.R    # Walk-forward validation
│   │   ├── signals/
│   │   │   ├── momentum.R        # Momentum signal module
│   │   │   └── mean_reversion.R  # Mean-reversion signal module
│   │   └── risk/
│   │       ├── position_size.R   # Kelly and fixed-fraction sizing
│   │       ├── drawdown.R        # Running drawdown monitor
│   │       └── var.R             # Historical VaR computation
│   └── view/
│       ├── backtest_tab.R        # Backtest results UI + server
│       ├── signals_tab.R         # Live signal dashboard UI + server
│       └── risk_tab.R            # Risk monitor UI + server
├── tests/
│   └── testthat/
│       ├── test-engine.R
│       ├── test-metrics.R
│       └── test-risk.R
├── renv.lock
└── rhino.yml</code></pre>
<p>The <code>logic/</code> layer contains no Shiny code. Every function in it can be called from a test, from the Shiny server, or from a batch job. This is what makes the difference between a trading dashboard that a single developer can maintain and one that becomes unmaintainable when the original author leaves.</p>
</section>
<section id="data-sources-available-to-kenyan-quants" class="level2">
<h2 class="anchored" data-anchor-id="data-sources-available-to-kenyan-quants">Data Sources Available to Kenyan Quants</h2>
<p>Before building any of the other components, the data layer determines what is actually possible. Here is what is available without paying for Bloomberg.</p>
<p>For NSE equities, the NSE website publishes daily brokerage trade reports in CSV format. The <code>quantmod</code> package can pull some emerging market data. For more complete NSE historical data, services like Africa Financial Data (Africagis) and some local brokers provide API access. The <code>httr2</code> package handles any REST-based data ingestion cleanly.</p>
<p>For forex, MetaTrader 5’s built-in history export is the most practical starting point for retail quants. Any broker running MT5 stores tick and OHLCV data that can be exported programmatically. The <code>RMT5</code> community package provides a bridge; alternatively, exporting to CSV via MT5’s scripting interface and ingesting with <code>readr</code> is reliable and broker-agnostic.</p>
<p>For macro data, the World Bank API is accessible via the <code>wbstats</code> package. The Central Bank of Kenya publishes rate decisions, money supply data, and exchange rate history on its website, accessible via <code>rvest</code> for scraping or via direct CSV download where structured formats are available.</p>
</section>
<section id="vectorised-backtesting-in-r" class="level2">
<h2 class="anchored" data-anchor-id="vectorised-backtesting-in-r">Vectorised Backtesting in R</h2>
<p>The backtesting engine is where most independent quants spend the most time and make the most mistakes. A vectorised approach using <code>xts</code> and <code>PerformanceAnalytics</code> is both fast and statistically defensible. The core engine lives in <code>app/logic/backtest/engine.R</code>:</p>
<div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb2-1">box<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">use</span>(</span>
<span id="cb2-2">  xts[xts, endpoints, period.apply],</span>
<span id="cb2-3">  zoo[coredata, index],</span>
<span id="cb2-4">  PerformanceAnalytics[Return.calculate, SharpeRatio, maxDrawdown, CalmarRatio],</span>
<span id="cb2-5">  dplyr[tibble, mutate, lag],</span>
<span id="cb2-6">  TTR[SMA, RSI, MACD]</span>
<span id="cb2-7">)</span>
<span id="cb2-8"></span>
<span id="cb2-9"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' Run a vectorised backtest on OHLCV data</span></span>
<span id="cb2-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#'</span></span>
<span id="cb2-11"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @param ohlcv     An xts object with OHLC columns and a Volume column</span></span>
<span id="cb2-12"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @param signal_fn A function that takes ohlcv and returns a numeric signal</span></span>
<span id="cb2-13"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#'                  vector: +1 (long), -1 (short), 0 (flat)</span></span>
<span id="cb2-14"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @param cost_bps  Round-trip transaction cost in basis points (default 5)</span></span>
<span id="cb2-15"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @return A list with equity curve (xts), trade log (tibble), and metrics</span></span>
<span id="cb2-16">run_backtest <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(ohlcv, signal_fn, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">cost_bps =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>) {</span>
<span id="cb2-17"></span>
<span id="cb2-18">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Generate signals from close prices; strictly lagged to avoid look-ahead</span></span>
<span id="cb2-19">  signal <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">signal_fn</span>(ohlcv)</span>
<span id="cb2-20">  signal_lagged <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">lag</span>(signal, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)          <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Apply signal on next bar open</span></span>
<span id="cb2-21">  signal_lagged[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>] <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>                    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># No position on first bar</span></span>
<span id="cb2-22"></span>
<span id="cb2-23">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Daily returns from close-to-close</span></span>
<span id="cb2-24">  close_prices  <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> ohlcv[, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Close"</span>]</span>
<span id="cb2-25">  daily_returns <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">Return.calculate</span>(close_prices, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">method =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"discrete"</span>)</span>
<span id="cb2-26">  daily_returns[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>] <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span></span>
<span id="cb2-27"></span>
<span id="cb2-28">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Strategy returns: signal * next-bar return, minus costs on position changes</span></span>
<span id="cb2-29">  position_change <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">abs</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">diff</span>(signal_lagged))</span>
<span id="cb2-30">  position_change[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>] <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span></span>
<span id="cb2-31">  cost_drag <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> position_change <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> (cost_bps <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10000</span>)</span>
<span id="cb2-32"></span>
<span id="cb2-33">  strategy_returns <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> (signal_lagged <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">coredata</span>(daily_returns)) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> cost_drag</span>
<span id="cb2-34"></span>
<span id="cb2-35">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Build equity curve from strategy returns</span></span>
<span id="cb2-36">  equity_xts <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">xts</span>(</span>
<span id="cb2-37">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">cumprod</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> strategy_returns) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10000</span>,   <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Starting with 10,000 units</span></span>
<span id="cb2-38">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">order.by =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">index</span>(ohlcv)</span>
<span id="cb2-39">  )</span>
<span id="cb2-40">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">colnames</span>(equity_xts) <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"equity"</span></span>
<span id="cb2-41"></span>
<span id="cb2-42">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Compute performance metrics</span></span>
<span id="cb2-43">  ann_factor <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">252</span>   <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Trading days per year</span></span>
<span id="cb2-44">  metrics <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(</span>
<span id="cb2-45">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">sharpe_ratio  =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">SharpeRatio</span>(strategy_returns, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">annualize =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>,</span>
<span id="cb2-46">                                <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">FUN =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"StdDev"</span>)[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>],</span>
<span id="cb2-47">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">max_drawdown  =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">maxDrawdown</span>(strategy_returns),</span>
<span id="cb2-48">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">calmar_ratio  =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">CalmarRatio</span>(strategy_returns),</span>
<span id="cb2-49">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">total_return  =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.numeric</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tail</span>(equity_xts, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10000</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>,</span>
<span id="cb2-50">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">n_trades      =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sum</span>(position_change <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">na.rm =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>)</span>
<span id="cb2-51">  )</span>
<span id="cb2-52"></span>
<span id="cb2-53">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(</span>
<span id="cb2-54">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">equity  =</span> equity_xts,</span>
<span id="cb2-55">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">returns =</span> strategy_returns,</span>
<span id="cb2-56">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">metrics =</span> metrics</span>
<span id="cb2-57">  )</span>
<span id="cb2-58">}</span></code></pre></div>
<p>One implementation detail matters more than any other: the <code>lag(signal, 1)</code> call on line 13. This is the look-ahead barrier. The signal computed on bar <img src="https://latex.codecogs.com/png.latex?t"> is applied to bar <img src="https://latex.codecogs.com/png.latex?t+1">, which is the earliest possible execution. Skipping this step is the most common source of unrealistic backtest results. The strategy appears to execute at the same price it used to generate the signal, which is impossible in live trading.</p>
</section>
<section id="a-signal-module-momentum-with-trend-filter" class="level2">
<h2 class="anchored" data-anchor-id="a-signal-module-momentum-with-trend-filter">A Signal Module: Momentum with Trend Filter</h2>
<p>The signal module in <code>app/logic/signals/momentum.R</code> implements a simple but testable momentum strategy. The filter prevents trading against a longer-term trend, a common technique for reducing whipsaw in trending markets:</p>
<div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb3-1">box<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">use</span>(</span>
<span id="cb3-2">  TTR[SMA, RSI],</span>
<span id="cb3-3">  zoo[coredata]</span>
<span id="cb3-4">)</span>
<span id="cb3-5"></span>
<span id="cb3-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' Momentum signal with 200-day trend filter</span></span>
<span id="cb3-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#'</span></span>
<span id="cb3-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @param ohlcv  xts OHLCV object</span></span>
<span id="cb3-9"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @param fast   Fast SMA window (default 20)</span></span>
<span id="cb3-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @param slow   Slow SMA window (default 50)</span></span>
<span id="cb3-11"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @param trend  Long-term trend SMA window (default 200)</span></span>
<span id="cb3-12"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @param rsi_w  RSI window for overbought/oversold filter (default 14)</span></span>
<span id="cb3-13"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @return Numeric vector: +1 (long), -1 (short), 0 (flat)</span></span>
<span id="cb3-14">momentum_signal <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(ohlcv, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">fast =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">20</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">slow =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">50</span>,</span>
<span id="cb3-15">                            <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">trend =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">200</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">rsi_w =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">14</span>) {</span>
<span id="cb3-16">  close  <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">coredata</span>(ohlcv[, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Close"</span>])</span>
<span id="cb3-17">  sma_f  <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">SMA</span>(close, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">n =</span> fast)</span>
<span id="cb3-18">  sma_s  <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">SMA</span>(close, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">n =</span> slow)</span>
<span id="cb3-19">  sma_t  <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">SMA</span>(close, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">n =</span> trend)</span>
<span id="cb3-20">  rsi    <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">RSI</span>(close, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">n =</span> rsi_w)</span>
<span id="cb3-21"></span>
<span id="cb3-22">  n      <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">length</span>(close)</span>
<span id="cb3-23">  signal <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">numeric</span>(n)</span>
<span id="cb3-24"></span>
<span id="cb3-25">  <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">for</span> (i <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">in</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">seq_len</span>(n)) {</span>
<span id="cb3-26">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> (<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">is.na</span>(sma_f[i]) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">||</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">is.na</span>(sma_s[i]) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">||</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">is.na</span>(sma_t[i])) <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">next</span></span>
<span id="cb3-27"></span>
<span id="cb3-28">    above_trend <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> close[i] <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> sma_t[i]</span>
<span id="cb3-29">    momentum_up <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> sma_f[i] <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> sma_s[i]</span>
<span id="cb3-30"></span>
<span id="cb3-31">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Long: above trend, momentum positive, not overbought</span></span>
<span id="cb3-32">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> (above_trend <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&amp;&amp;</span> momentum_up <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&amp;&amp;</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">is.na</span>(rsi[i]) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&amp;&amp;</span> rsi[i] <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">70</span>) {</span>
<span id="cb3-33">      signal[i] <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>L</span>
<span id="cb3-34">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Short: below trend, momentum negative, not oversold</span></span>
<span id="cb3-35">    } <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">else</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> (<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!</span>above_trend <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&amp;&amp;</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!</span>momentum_up <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&amp;&amp;</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">is.na</span>(rsi[i]) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&amp;&amp;</span> rsi[i] <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">30</span>) {</span>
<span id="cb3-36">      signal[i] <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>L</span>
<span id="cb3-37">    }</span>
<span id="cb3-38">  }</span>
<span id="cb3-39"></span>
<span id="cb3-40">  signal</span>
<span id="cb3-41">}</span></code></pre></div>
<p>This signal function takes an <code>xts</code> object and returns a plain numeric vector. No Shiny dependencies, no global state. The <code>tests/testthat/test-engine.R</code> file can call <code>momentum_signal()</code> directly on synthetic data and assert that the output has the right length, contains only -1, 0, and 1, and returns 0 for the first 199 bars where the trend filter has insufficient history.</p>
</section>
<section id="risk-monitor-position-sizing-and-live-drawdown" class="level2">
<h2 class="anchored" data-anchor-id="risk-monitor-position-sizing-and-live-drawdown">Risk Monitor: Position Sizing and Live Drawdown</h2>
<p>The risk monitor is the component most independent traders skip and most regret skipping. The position sizing module in <code>app/logic/risk/position_size.R</code> implements both fractional Kelly and fixed-fraction sizing, giving the trader control over how aggressively the system bets:</p>
<div class="sourceCode" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb4-1">box<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">use</span>(dplyr[...])</span>
<span id="cb4-2"></span>
<span id="cb4-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' Compute position size using fractional Kelly criterion</span></span>
<span id="cb4-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#'</span></span>
<span id="cb4-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @param win_rate    Historical win rate (proportion, 0 to 1)</span></span>
<span id="cb4-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @param win_loss_r  Average win-to-loss ratio</span></span>
<span id="cb4-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @param kelly_frac  Kelly fraction to apply (0.25 = quarter-Kelly)</span></span>
<span id="cb4-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @param account_eq  Current account equity in base currency</span></span>
<span id="cb4-9"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @param price       Current asset price</span></span>
<span id="cb4-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @return Recommended position size in units</span></span>
<span id="cb4-11">kelly_position_size <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(win_rate, win_loss_r,</span>
<span id="cb4-12">                                <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">kelly_frac =</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.25</span>,</span>
<span id="cb4-13">                                account_eq, price) {</span>
<span id="cb4-14">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Full Kelly fraction of capital to risk</span></span>
<span id="cb4-15">  full_kelly <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> (win_rate <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> win_loss_r <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> (<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> win_rate)) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> win_loss_r</span>
<span id="cb4-16">  fractional_kelly <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">max</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, full_kelly <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> kelly_frac)</span>
<span id="cb4-17"></span>
<span id="cb4-18">  capital_at_risk <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> account_eq <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> fractional_kelly</span>
<span id="cb4-19">  units <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">floor</span>(capital_at_risk <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> price)</span>
<span id="cb4-20">  units</span>
<span id="cb4-21">}</span>
<span id="cb4-22"></span>
<span id="cb4-23"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' Check if drawdown circuit breakers are active</span></span>
<span id="cb4-24"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#'</span></span>
<span id="cb4-25"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @param current_equity  Current account equity</span></span>
<span id="cb4-26"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @param peak_equity     Highest recorded equity</span></span>
<span id="cb4-27"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @param soft_limit_pct  Drawdown % that triggers reduced sizing (default 0.10)</span></span>
<span id="cb4-28"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @param hard_limit_pct  Drawdown % that halts all trading (default 0.20)</span></span>
<span id="cb4-29"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @return A list with status ("normal", "reduced", "halted") and drawdown_pct</span></span>
<span id="cb4-30">check_circuit_breakers <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(current_equity, peak_equity,</span>
<span id="cb4-31">                                   <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">soft_limit_pct =</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.10</span>,</span>
<span id="cb4-32">                                   <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">hard_limit_pct =</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.20</span>) {</span>
<span id="cb4-33">  dd_pct <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> (peak_equity <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> current_equity) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> peak_equity</span>
<span id="cb4-34"></span>
<span id="cb4-35">  status <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> dplyr<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">case_when</span>(</span>
<span id="cb4-36">    dd_pct <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;=</span> hard_limit_pct <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">~</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"halted"</span>,</span>
<span id="cb4-37">    dd_pct <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;=</span> soft_limit_pct <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">~</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"reduced"</span>,</span>
<span id="cb4-38">    <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>                     <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">~</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"normal"</span></span>
<span id="cb4-39">  )</span>
<span id="cb4-40"></span>
<span id="cb4-41">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">status =</span> status, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">drawdown_pct =</span> dd_pct)</span>
<span id="cb4-42">}</span></code></pre></div>
<p>The circuit breaker logic is what separates a risk monitor from a reporting tool. When <code>status</code> is <code>"halted"</code>, the signal dashboard should display a hard stop and the system should not generate new position recommendations regardless of what the signal module outputs. This logic runs before signal generation in the Shiny server, not after.</p>
<p>For more on position sizing foundations, see the <a href="../../../blog/posts/position-sizing-kelly-criterion-forex-r/">Kelly criterion and position sizing guide</a>.</p>
</section>
<section id="what-this-stack-cannot-do-honest-limitations" class="level2">
<h2 class="anchored" data-anchor-id="what-this-stack-cannot-do-honest-limitations">What This Stack Cannot Do: Honest Limitations</h2>
<p>Any serious treatment of this topic requires acknowledging where R-based independent infrastructure falls short of institutional systems.</p>
<p><strong>Latency.</strong> R is not a low-latency execution environment. Institutional high-frequency trading systems operate in microseconds using co-located C++ or FPGA hardware. This stack targets end-of-day or intraday strategies with holding periods measured in minutes or longer. If your edge requires sub-second execution, this is the wrong tool. For NSE equities, where the market operates in a relatively low-frequency environment, end-of-day and intraday signals on the 5-minute or 15-minute timeframe are entirely realistic targets.</p>
<p><strong>Data quality.</strong> NSE historical data has gaps, corporate action adjustments that are not always clean, and limited tick-level granularity. Any backtest using NSE data needs explicit handling of trading halts, delisting events, and dividend adjustments. Survivorship bias is a real risk if you build a universe of NSE stocks using the current listing and backtest historically.</p>
<p><strong>Execution integration.</strong> This stack produces signals and monitors risk, but it does not execute trades directly. Integration with an execution layer (MT5 via MetaSocket, or a broker API for NSE equities) adds engineering complexity that goes beyond the scope of a single Shiny application.</p>
<p><strong>Regulatory compliance.</strong> The Capital Markets Authority has specific requirements for algorithmic trading in Kenya. Independent quants should review the CMA’s framework before deploying any live automated system, even for personal account trading.</p>
</section>
<section id="the-infrastructure-cost-comparison" class="level2">
<h2 class="anchored" data-anchor-id="the-infrastructure-cost-comparison">The Infrastructure Cost Comparison</h2>
<p>To make the cost contrast concrete: running this full stack in production costs between $24 and $48 per month on a DigitalOcean or Hetzner VPS. The lower end handles a single-user setup with one or two strategies. The higher end covers a small team, a PostgreSQL instance for trade logging, and enough headroom for backtesting runs that crunch several years of daily data.</p>
<p>Bloomberg Terminal: approximately $2,083 per month. FactSet: approximately $1,000 to $2,000 per month. A complete R/Shiny Rhino stack: under $50 per month, open-source tools, and full ownership of the code.</p>
<p>The remaining gap is not technology. It is data, and specifically tick-level or institutional-grade data for assets where the edge lives in microstructure. For strategies that operate on daily or hourly data, that gap effectively does not exist.</p>
<p>For the deployment side of this infrastructure, the <a href="../../../blog/posts/rshiny-hosting-guide/">R Shiny hosting guide</a> covers the options from shinyapps.io to self-hosted Docker containers in detail. For the statistical rigour that the backtesting metrics need, the <a href="../../../blog/posts/deflated-sharpe-ratio/">Deflated Sharpe Ratio</a> post explains why the metrics this engine produces require adjustment before you trust them for strategy selection.</p>
</section>
<section id="the-real-question-for-kenyan-quants" class="level2">
<h2 class="anchored" data-anchor-id="the-real-question-for-kenyan-quants">The Real Question for Kenyan Quants</h2>
<p>The technology case for building institutional-grade trading infrastructure in R is clear. The packages exist, the deployment patterns work, and the cost is within reach of any serious independent practitioner.</p>
<p>The harder question is whether independent Kenyan quants are building anything at all, or whether the perception that institutional infrastructure is inaccessible is itself the barrier. The quant gap between a retail trader in Nairobi and a systematic desk in Johannesburg is closing from both directions: retail tools are improving, and institutional alpha in liquid markets is compressing. The window where independent quants in East Africa can build systematic edges with these tools is open now.</p>
<p>What stops most independent quants from building this is not capability. It is starting.</p>
<hr>
<p><em>Kwiz Computing Technologies builds production R/Shiny and Rhino applications for trading teams and data-intensive businesses across East Africa. If you are working on systematic trading infrastructure and want to discuss architecture, see how we approach <a href="../../../blog/posts/systematic-trading-r/">systematic trading in R</a> or how the <a href="../../../blog/posts/quant-gap-african-retail-traders-vs-institutional-algorithms-data-science/">quant gap between retail and institutional traders</a> is narrowing in Africa.</em></p>


</section>

 ]]></description>
  <category>enterprise-data-science</category>
  <category>quantitative-finance</category>
  <guid>https://kwizresearch.com/blog/posts/kenyan-quants-institutional-trading-systems-r-shiny-rhino/</guid>
  <pubDate>Thu, 23 Apr 2026 00:00:00 GMT</pubDate>
  <media:content url="https://kwizresearch.com/blog/posts/kenyan-quants-institutional-trading-systems-r-shiny-rhino/thumbnail.png" medium="image" type="image/png"/>
</item>
<item>
  <title>Where Environmental Science Meets Finance: Quantitative Methods for Carbon Markets</title>
  <dc:creator>Kwizera Jean</dc:creator>
  <link>https://kwizresearch.com/blog/posts/carbon-markets-quant-methods/</link>
  <description><![CDATA[ 




<section id="the-convergence" class="level2">
<h2 class="anchored" data-anchor-id="the-convergence">The Convergence</h2>
<p>At Kwiz Computing Technologies, our two deepest areas of expertise — environmental data science and quantitative finance — are converging in an unexpected place: <strong>carbon markets</strong>.</p>
<p>Carbon markets are financial instruments built on environmental science. A carbon credit represents a tonne of CO2 equivalent that has been avoided or removed. Its value depends on environmental integrity (was the emission reduction real?) and market dynamics (what will buyers pay?). Analysing these markets requires both environmental domain knowledge and quantitative financial methods.</p>
<p>This post explores how the same statistical frameworks we use in systematic trading — time series analysis, regime detection, cross-validation — apply to the emerging carbon market ecosystem.</p>
</section>
<section id="the-carbon-market-landscape" class="level2">
<h2 class="anchored" data-anchor-id="the-carbon-market-landscape">The Carbon Market Landscape</h2>
<section id="compliance-markets" class="level3">
<h3 class="anchored" data-anchor-id="compliance-markets">Compliance Markets</h3>
<p>Regulated carbon markets — the EU Emissions Trading System (EU ETS), California’s cap-and-trade, and emerging systems in China and elsewhere — set a cap on total emissions and allow companies to trade emission allowances. These markets are mature, liquid, and behave like traditional financial instruments.</p>
</section>
<section id="voluntary-carbon-markets" class="level3">
<h3 class="anchored" data-anchor-id="voluntary-carbon-markets">Voluntary Carbon Markets</h3>
<p>Voluntary markets allow organisations to purchase carbon credits (offsets) to compensate for emissions they cannot eliminate. These markets are less liquid, more heterogeneous, and face significant challenges around credit quality, additionality, and permanence.</p>
<p>Both markets generate time series data amenable to quantitative analysis, but with very different statistical properties.</p>
</section>
</section>
<section id="quantitative-approaches" class="level2">
<h2 class="anchored" data-anchor-id="quantitative-approaches">Quantitative Approaches</h2>
<section id="time-series-analysis-of-carbon-prices" class="level3">
<h3 class="anchored" data-anchor-id="time-series-analysis-of-carbon-prices">Time Series Analysis of Carbon Prices</h3>
<p>Carbon allowance prices (particularly EU ETS) exhibit many of the same statistical properties as other financial time series: volatility clustering, regime shifts, and sensitivity to policy announcements.</p>
<div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb1-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(forecast)</span>
<span id="cb1-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(tseries)</span>
<span id="cb1-3"></span>
<span id="cb1-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Load EU ETS carbon price data</span></span>
<span id="cb1-5">carbon_prices <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">read_csv</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"eu_ets_prices.csv"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb1-6">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mutate</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">log_return =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">log</span>(price <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">lag</span>(price)))</span>
<span id="cb1-7"></span>
<span id="cb1-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Test for stationarity</span></span>
<span id="cb1-9"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">adf.test</span>(carbon_prices<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>log_return, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">alternative =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"stationary"</span>)</span>
<span id="cb1-10"></span>
<span id="cb1-11"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># GARCH modelling for volatility dynamics</span></span>
<span id="cb1-12"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(rugarch)</span>
<span id="cb1-13"></span>
<span id="cb1-14">spec <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ugarchspec</span>(</span>
<span id="cb1-15">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">variance.model =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">model =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"sGARCH"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">garchOrder =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)),</span>
<span id="cb1-16">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">mean.model =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">armaOrder =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)),</span>
<span id="cb1-17">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">distribution.model =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"std"</span>  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Student-t for fat tails</span></span>
<span id="cb1-18">)</span>
<span id="cb1-19"></span>
<span id="cb1-20">fit <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ugarchfit</span>(spec, carbon_prices<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>log_return)</span></code></pre></div>
<p>GARCH models capture the volatility clustering that characterises carbon markets — periods of low volatility interrupted by policy-driven spikes. This is directly analogous to our forex strategy work, where volatility regime modelling informs position sizing and risk management.</p>
</section>
<section id="regime-detection-with-hidden-markov-models" class="level3">
<h3 class="anchored" data-anchor-id="regime-detection-with-hidden-markov-models">Regime Detection with Hidden Markov Models</h3>
<p>Carbon markets exhibit distinct regimes driven by policy changes, economic cycles, and regulatory announcements. Hidden Markov Models (HMMs) identify these regimes statistically:</p>
<div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb2-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(depmixS4)</span>
<span id="cb2-2"></span>
<span id="cb2-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Fit a 3-state HMM to carbon returns</span></span>
<span id="cb2-4">hmm_model <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">depmix</span>(</span>
<span id="cb2-5">  log_return <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">~</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>,</span>
<span id="cb2-6">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">data =</span> carbon_prices,</span>
<span id="cb2-7">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">nstates =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>,</span>
<span id="cb2-8">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">family =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">gaussian</span>()</span>
<span id="cb2-9">)</span>
<span id="cb2-10"></span>
<span id="cb2-11">hmm_fit <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">fit</span>(hmm_model)</span>
<span id="cb2-12"></span>
<span id="cb2-13"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Extract regime probabilities</span></span>
<span id="cb2-14">regimes <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">posterior</span>(hmm_fit)</span>
<span id="cb2-15">carbon_prices<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>regime <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> regimes<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>state</span></code></pre></div>
<p>Typical findings reveal:</p>
<ul>
<li><strong>Low-volatility regime</strong> — stable pricing during “business as usual” periods</li>
<li><strong>Transition regime</strong> — moderate volatility during policy deliberation</li>
<li><strong>High-volatility regime</strong> — sharp price movements around regulatory decisions</li>
</ul>
<p>This is the same HMM framework we use for carry strategy regime detection in Kwiz Quants. The mathematics is identical; only the domain interpretation changes.</p>
</section>
<section id="seasonal-decomposition" class="level3">
<h3 class="anchored" data-anchor-id="seasonal-decomposition">Seasonal Decomposition</h3>
<p>Carbon markets exhibit strong seasonal patterns driven by compliance cycles. EU ETS prices typically rise toward April surrender deadlines and experience sell-offs afterward.</p>
<div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb3-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(feasts)</span>
<span id="cb3-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(tsibble)</span>
<span id="cb3-3"></span>
<span id="cb3-4">carbon_tsibble <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> carbon_prices <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb3-5">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as_tsibble</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">index =</span> date)</span>
<span id="cb3-6"></span>
<span id="cb3-7">decomposition <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> carbon_tsibble <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb3-8">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">model</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">STL</span>(price <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">~</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">season</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">period =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">252</span>))) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span>  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Trading days</span></span>
<span id="cb3-9">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">components</span>()</span>
<span id="cb3-10"></span>
<span id="cb3-11"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">autoplot</span>(decomposition)</span></code></pre></div>
<p>Understanding seasonal patterns helps both market participants (timing purchases) and environmental project developers (timing credit issuance).</p>
</section>
</section>
<section id="environmental-data-meets-market-data" class="level2">
<h2 class="anchored" data-anchor-id="environmental-data-meets-market-data">Environmental Data Meets Market Data</h2>
<section id="linking-monitoring-to-valuation" class="level3">
<h3 class="anchored" data-anchor-id="linking-monitoring-to-valuation">Linking Monitoring to Valuation</h3>
<p>The most interesting analytical opportunity in carbon markets lies at the intersection of environmental monitoring data and market data. Satellite-derived vegetation indices (NDVI), ground-sensor emissions data, and weather patterns all affect the <em>supply side</em> of carbon credits, while regulatory and economic factors drive <em>demand</em>.</p>
<div class="sourceCode" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb4-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(terra)</span>
<span id="cb4-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(sf)</span>
<span id="cb4-3"></span>
<span id="cb4-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Process satellite data for reforestation project</span></span>
<span id="cb4-5">ndvi_timeseries <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">rast</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"project_ndvi_2020_2025.tif"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb4-6">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">extract</span>(project_boundary) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb4-7">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">summarise</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">mean_ndvi =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mean</span>(NDVI, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">na.rm =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">.by =</span> date)</span>
<span id="cb4-8"></span>
<span id="cb4-9"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Correlate vegetation growth with credit issuance</span></span>
<span id="cb4-10">correlation_analysis <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">merge</span>(</span>
<span id="cb4-11">  ndvi_timeseries,</span>
<span id="cb4-12">  credit_issuance,</span>
<span id="cb4-13">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">by =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"date"</span></span>
<span id="cb4-14">)</span>
<span id="cb4-15"></span>
<span id="cb4-16"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">cor.test</span>(</span>
<span id="cb4-17">  correlation_analysis<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>mean_ndvi,</span>
<span id="cb4-18">  correlation_analysis<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>credits_issued</span>
<span id="cb4-19">)</span></code></pre></div>
<p>This analysis connects our environmental remote sensing capabilities (satellite data processing, geospatial analysis) with financial valuation — a combination that is rare in the market and increasingly valuable.</p>
</section>
<section id="statistical-verification-of-additionality" class="level3">
<h3 class="anchored" data-anchor-id="statistical-verification-of-additionality">Statistical Verification of Additionality</h3>
<p>“Additionality” — whether a carbon project’s emission reductions would have happened anyway — is the central credibility question in voluntary carbon markets. Statistical methods can help assess this:</p>
<div class="sourceCode" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb5-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Synthetic control method for additionality assessment</span></span>
<span id="cb5-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(Synth)</span>
<span id="cb5-3"></span>
<span id="cb5-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Compare project region against similar non-project regions</span></span>
<span id="cb5-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># to estimate counterfactual emissions</span></span>
<span id="cb5-6">synth_result <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">synth</span>(</span>
<span id="cb5-7">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">dataprep.out =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">dataprep</span>(</span>
<span id="cb5-8">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">foo =</span> emissions_data,</span>
<span id="cb5-9">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">predictors =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"gdp"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"population"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"forest_cover"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"temperature"</span>),</span>
<span id="cb5-10">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">dependent =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"emissions"</span>,</span>
<span id="cb5-11">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">unit.variable =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"region_id"</span>,</span>
<span id="cb5-12">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">time.variable =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"year"</span>,</span>
<span id="cb5-13">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">treatment.identifier =</span> project_region,</span>
<span id="cb5-14">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">controls.identifier =</span> control_regions,</span>
<span id="cb5-15">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">time.predictors.prior =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2015</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2019</span>,</span>
<span id="cb5-16">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">time.optimize.ssr =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2015</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2019</span>,</span>
<span id="cb5-17">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">time.plot =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2015</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2025</span></span>
<span id="cb5-18">  )</span>
<span id="cb5-19">)</span></code></pre></div>
<p>Synthetic control methods create a statistical counterfactual — what would emissions have been without the project? — by constructing a weighted combination of similar regions. This is more rigorous than the simple before-and-after comparisons that many carbon projects use for verification.</p>
</section>
</section>
<section id="the-kwiz-approach" class="level2">
<h2 class="anchored" data-anchor-id="the-kwiz-approach">The Kwiz Approach</h2>
<p>Carbon markets represent a natural convergence of our two core competencies:</p>
<ol type="1">
<li><p><strong>Environmental data science</strong> provides the domain knowledge to assess project quality, process satellite monitoring data, and understand the science behind emission reductions.</p></li>
<li><p><strong>Quantitative finance</strong> provides the statistical framework for price modelling, regime detection, risk assessment, and systematic analysis of market dynamics.</p></li>
</ol>
<p>We see carbon markets as a domain where interdisciplinary expertise — rather than pure financial engineering or pure environmental science alone — creates the most value. Organisations navigating these markets need partners who understand both the environmental integrity of carbon credits and the financial dynamics of the markets where they trade.</p>
</section>
<section id="looking-forward" class="level2">
<h2 class="anchored" data-anchor-id="looking-forward">Looking Forward</h2>
<p>As carbon markets mature and expand — the voluntary market alone is projected to grow substantially over the coming decade — the demand for rigorous, data-driven analysis will only increase. The organisations and projects that adopt quantitative methods for monitoring, verification, and market analysis will have a significant advantage over those relying on qualitative assessments and rules of thumb.</p>
<p>At Kwiz Computing, we are positioning ourselves at this intersection — applying the same engineering discipline and statistical rigour that characterises our trading infrastructure and environmental analytics to the carbon market ecosystem. It is, in many ways, the project that best represents what we do: building intelligent systems at the boundary between environmental science and finance.</p>


</section>

 ]]></description>
  <category>Environmental Data Science</category>
  <category>Quantitative Finance</category>
  <category>Carbon Markets</category>
  <category>R</category>
  <guid>https://kwizresearch.com/blog/posts/carbon-markets-quant-methods/</guid>
  <pubDate>Sun, 15 Mar 2026 00:00:00 GMT</pubDate>
</item>
<item>
  <title>Backtesting Without Lookahead Bias: Combinatorial Purged Cross-Validation</title>
  <dc:creator>Kwizera Jean</dc:creator>
  <link>https://kwizresearch.com/blog/posts/combinatorial-purged-cv/</link>
  <description><![CDATA[ 




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


</section>

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




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


</section>

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




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


</section>

 ]]></description>
  <category>Quantitative Finance</category>
  <category>R</category>
  <category>Kwiz Quants</category>
  <guid>https://kwizresearch.com/blog/posts/systematic-trading-r/</guid>
  <pubDate>Sat, 10 Jan 2026 00:00:00 GMT</pubDate>
</item>
<item>
  <title>Assessing Biodiversity Data Quality: A GBIF Kenya Case Study</title>
  <dc:creator>Kwizera Jean</dc:creator>
  <link>https://kwizresearch.com/blog/posts/gbif-kenya-data-quality/</link>
  <description><![CDATA[ 




<section id="the-data-quality-challenge-in-biodiversity-science" class="level2">
<h2 class="anchored" data-anchor-id="the-data-quality-challenge-in-biodiversity-science">The Data Quality Challenge in Biodiversity Science</h2>
<p>The Global Biodiversity Information Facility (GBIF) aggregates hundreds of millions of species occurrence records from institutions worldwide. For Kenya alone, GBIF holds millions of records spanning decades of observation. But aggregation at this scale introduces systematic data quality challenges that can undermine conservation decisions if left unaddressed.</p>
<p>In our work with environmental impact assessments and biodiversity monitoring across East Africa, we have seen how uncritical use of GBIF data leads to flawed baseline assessments. This post walks through our approach to systematic data quality assessment — a reproducible R pipeline that we apply to every biodiversity project.</p>
</section>
<section id="why-data-quality-matters" class="level2">
<h2 class="anchored" data-anchor-id="why-data-quality-matters">Why Data Quality Matters</h2>
<p>Biodiversity data feeds directly into conservation planning, EIA baseline studies, and policy decisions. When that data contains coordinate errors, taxonomic misidentifications, or temporal gaps, the downstream consequences are real: protected areas drawn around phantom populations, impact assessments that miss genuinely sensitive species, and monitoring programmes built on unreliable baselines.</p>
<p>The challenge is that GBIF data comes from heterogeneous sources — museum collections, citizen science platforms, academic surveys — each with different quality standards and error profiles.</p>
</section>
<section id="a-reproducible-quality-assessment-workflow" class="level2">
<h2 class="anchored" data-anchor-id="a-reproducible-quality-assessment-workflow">A Reproducible Quality Assessment Workflow</h2>
<p>Our pipeline operates in four stages, each implemented as testable R functions:</p>
<section id="stage-1-data-retrieval-and-initial-profiling" class="level3">
<h3 class="anchored" data-anchor-id="stage-1-data-retrieval-and-initial-profiling">Stage 1: Data Retrieval and Initial Profiling</h3>
<p>We use the <code>rgbif</code> package to access GBIF’s API programmatically, then profile the dataset to understand its composition before applying any filters.</p>
<div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb1-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(rgbif)</span>
<span id="cb1-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(dplyr)</span>
<span id="cb1-3"></span>
<span id="cb1-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Retrieve occurrence data for Kenya</span></span>
<span id="cb1-5">occurrences <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">occ_search</span>(</span>
<span id="cb1-6">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">country =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"KE"</span>,</span>
<span id="cb1-7">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">hasCoordinate =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>,</span>
<span id="cb1-8">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">limit =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">50000</span></span>
<span id="cb1-9">)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>data</span>
<span id="cb1-10"></span>
<span id="cb1-11"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Initial profiling</span></span>
<span id="cb1-12">profiling <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> occurrences <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb1-13">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">summarise</span>(</span>
<span id="cb1-14">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">n_records =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">n</span>(),</span>
<span id="cb1-15">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">n_species =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">n_distinct</span>(species),</span>
<span id="cb1-16">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">date_range =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">paste</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">min</span>(year, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">na.rm =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>), <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">max</span>(year, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">na.rm =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">sep =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"-"</span>),</span>
<span id="cb1-17">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">pct_with_coords =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mean</span>(<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">is.na</span>(decimalLatitude)) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100</span></span>
<span id="cb1-18">  )</span></code></pre></div>
</section>
<section id="stage-2-coordinate-validation" class="level3">
<h3 class="anchored" data-anchor-id="stage-2-coordinate-validation">Stage 2: Coordinate Validation</h3>
<p>Coordinate errors are the most common and consequential data quality issue. We check for:</p>
<ul>
<li><strong>Country centroid records</strong> — coordinates that fall exactly on Kenya’s geographic centroid, typically indicating missing data that was auto-filled</li>
<li><strong>Zero coordinates</strong> — records at (0, 0) or with zero latitude/longitude</li>
<li><strong>Precision issues</strong> — coordinates rounded to integer degrees, indicating low spatial precision</li>
<li><strong>Out-of-boundary records</strong> — coordinates that fall outside Kenya’s borders despite being tagged as Kenyan occurrences</li>
</ul>
<div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb2-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(sf)</span>
<span id="cb2-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(CoordinateCleaner)</span>
<span id="cb2-3"></span>
<span id="cb2-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Flag coordinate issues</span></span>
<span id="cb2-5">cleaned <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> occurrences <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb2-6">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">cc_val</span>() <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span>         <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Remove invalid coordinates</span></span>
<span id="cb2-7">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">cc_zero</span>() <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span>        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Flag zero coordinates</span></span>
<span id="cb2-8">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">cc_cen</span>() <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span>         <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Flag country centroids</span></span>
<span id="cb2-9">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">cc_dupl</span>() <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span>        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Flag exact duplicates</span></span>
<span id="cb2-10">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">cc_gbif</span>() <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span>        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Flag GBIF headquarters</span></span>
<span id="cb2-11">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">cc_inst</span>()           <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Flag biodiversity institutions</span></span></code></pre></div>
</section>
<section id="stage-3-taxonomic-verification" class="level3">
<h3 class="anchored" data-anchor-id="stage-3-taxonomic-verification">Stage 3: Taxonomic Verification</h3>
<p>Taxonomic names change over time as species are reclassified. We validate names against the GBIF backbone taxonomy and flag records with unresolved or disputed classifications.</p>
<div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb3-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Verify taxonomy against GBIF backbone</span></span>
<span id="cb3-2">taxa_check <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> occurrences <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb3-3">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">distinct</span>(species, taxonomicStatus, taxonRank) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb3-4">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">filter</span>(</span>
<span id="cb3-5">    taxonomicStatus <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"ACCEPTED"</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|</span></span>
<span id="cb3-6">    taxonRank <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"SPECIES"</span></span>
<span id="cb3-7">  )</span></code></pre></div>
</section>
<section id="stage-4-temporal-and-sampling-bias-assessment" class="level3">
<h3 class="anchored" data-anchor-id="stage-4-temporal-and-sampling-bias-assessment">Stage 4: Temporal and Sampling Bias Assessment</h3>
<p>Even clean data can be misleading if it is unevenly distributed in time or space. We assess:</p>
<ul>
<li><strong>Temporal coverage</strong> — Are records concentrated in particular years or seasons?</li>
<li><strong>Spatial sampling bias</strong> — Are records clustered near roads, cities, or research stations?</li>
<li><strong>Taxonomic bias</strong> — Are certain taxa over-represented relative to expected diversity?</li>
</ul>
</section>
</section>
<section id="key-findings-from-the-kenya-dataset" class="level2">
<h2 class="anchored" data-anchor-id="key-findings-from-the-kenya-dataset">Key Findings from the Kenya Dataset</h2>
<p>Applying this pipeline to Kenyan GBIF data consistently reveals several patterns:</p>
<ul>
<li>A significant fraction of records fall on country or county centroids, indicating geocoding from administrative labels rather than actual observation coordinates</li>
<li>Temporal coverage is heavily skewed toward recent decades, with sparse data before 2000</li>
<li>Spatial sampling is concentrated along major transport corridors and near Nairobi, with large areas of northern Kenya essentially unsampled</li>
<li>Bird records dominate the dataset, with invertebrates and plants substantially under-represented relative to their actual diversity</li>
</ul>
</section>
<section id="implications-for-practice" class="level2">
<h2 class="anchored" data-anchor-id="implications-for-practice">Implications for Practice</h2>
<p>These findings have direct implications for how GBIF data should be used in environmental assessments:</p>
<ol type="1">
<li><p><strong>Never use raw GBIF data without quality filtering.</strong> The proportion of records flagged by our pipeline typically ranges from 15-30% — enough to materially change any analysis.</p></li>
<li><p><strong>Document your filtering decisions.</strong> Reproducible pipelines ensure that quality decisions are transparent and auditable — critical for EIA submissions.</p></li>
<li><p><strong>Acknowledge sampling gaps.</strong> Absence of records does not mean absence of species. Reporting what the data <em>cannot</em> tell you is as important as what it can.</p></li>
<li><p><strong>Combine sources.</strong> GBIF data should be supplemented with targeted field surveys, especially in under-sampled regions.</p></li>
</ol>
</section>
<section id="building-better-tools" class="level2">
<h2 class="anchored" data-anchor-id="building-better-tools">Building Better Tools</h2>
<p>This work informs our broader commitment to environmental data transparency. Our open-source <a href="https://github.com/kwizresearch/kenyaEIAFetcher">kenyaEIAFetcher</a> R package applies similar principles to EIA data access, and we are working on additional tools to make environmental data quality assessment more accessible to practitioners across East Africa.</p>
<p>Reproducible, transparent data quality assessment is not just good practice — it is essential for environmental governance that works.</p>


</section>

 ]]></description>
  <category>Environmental Data Science</category>
  <category>Biodiversity</category>
  <category>Data Quality</category>
  <category>R</category>
  <guid>https://kwizresearch.com/blog/posts/gbif-kenya-data-quality/</guid>
  <pubDate>Tue, 25 Nov 2025 00:00:00 GMT</pubDate>
</item>
<item>
  <title>R for Green Architecture: Data-Driven Design in Informal Settlements</title>
  <dc:creator>Kwizera Jean</dc:creator>
  <link>https://kwizresearch.com/blog/posts/r-green-architecture-slums/</link>
  <description><![CDATA[ 




<section id="the-challenge-sustainable-design-in-resource-limited-contexts" class="level2">
<h2 class="anchored" data-anchor-id="the-challenge-sustainable-design-in-resource-limited-contexts">The Challenge: Sustainable Design in Resource-Limited Contexts</h2>
<p>Urban informal settlements house a significant and growing share of the world’s urban population. These communities face compounding environmental challenges: flooding, heat stress, poor air quality, water scarcity, and inadequate sanitation. Green architecture — environmentally conscious design that maximises human welfare while minimising environmental impact — offers promising solutions, but implementation requires data-driven planning that accounts for the unique constraints of informal settlement contexts.</p>
<p>R programming, with its statistical depth and geospatial capabilities, provides a powerful analytical framework for understanding these challenges and designing effective interventions.</p>
</section>
<section id="why-data-matters-in-informal-settlements" class="level2">
<h2 class="anchored" data-anchor-id="why-data-matters-in-informal-settlements">Why Data Matters in Informal Settlements</h2>
<p>Formal urban development benefits from extensive data: cadastral records, utility maps, building permits, and environmental monitoring networks. Informal settlements typically lack all of these. Planning green infrastructure in data-scarce environments requires creative analytical approaches — exactly where R’s ecosystem excels.</p>
<p>R enables practitioners to:</p>
<ul>
<li>Map and analyse settlement patterns using satellite imagery and spatial data, even without official documentation</li>
<li>Identify environmental vulnerabilities through statistical modelling and spatial analysis</li>
<li>Optimise resource distribution using mathematical optimisation</li>
<li>Establish rigorous evaluation frameworks for measuring intervention effectiveness</li>
</ul>
</section>
<section id="use-case-1-spatial-analysis-for-green-infrastructure-planning" class="level2">
<h2 class="anchored" data-anchor-id="use-case-1-spatial-analysis-for-green-infrastructure-planning">Use Case 1: Spatial Analysis for Green Infrastructure Planning</h2>
<p>Determining where to place green infrastructure (community gardens, bio-swales, tree planting, green roofs) requires understanding complex spatial relationships. We use <code>sf</code> and <code>leaflet</code> to model site suitability based on multiple factors:</p>
<div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb1-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(sf)</span>
<span id="cb1-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(leaflet)</span>
<span id="cb1-3"></span>
<span id="cb1-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Score potential sites based on multiple criteria</span></span>
<span id="cb1-5">sites <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> potential_locations <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb1-6">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mutate</span>(</span>
<span id="cb1-7">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">suitability =</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.3</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> drainage_score <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb1-8">                  <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.25</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> access_score <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb1-9">                  <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.2</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> sunlight_score <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb1-10">                  <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.15</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> soil_quality <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb1-11">                  <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.1</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> community_proximity,</span>
<span id="cb1-12">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">priority =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">case_when</span>(</span>
<span id="cb1-13">      suitability <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.7</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">~</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"High"</span>,</span>
<span id="cb1-14">      suitability <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.4</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">~</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Medium"</span>,</span>
<span id="cb1-15">      <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">~</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Low"</span></span>
<span id="cb1-16">    )</span>
<span id="cb1-17">  )</span>
<span id="cb1-18"></span>
<span id="cb1-19"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Interactive mapping for stakeholder engagement</span></span>
<span id="cb1-20"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">leaflet</span>(sites) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb1-21">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">addTiles</span>() <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb1-22">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">addCircleMarkers</span>(</span>
<span id="cb1-23">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">color =</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">~</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">priority_palette</span>(priority),</span>
<span id="cb1-24">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">popup =</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">~</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">paste</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Type:"</span>, infrastructure_type,</span>
<span id="cb1-25">                   <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"&lt;br&gt;Score:"</span>, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">round</span>(suitability, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>))</span>
<span id="cb1-26">  )</span></code></pre></div>
<p>Interactive maps are particularly valuable for stakeholder engagement in communities where technical reports may not be accessible. A visual tool that residents can interact with drives better participation and more informed decision-making.</p>
</section>
<section id="use-case-2-climate-vulnerability-and-green-roof-modelling" class="level2">
<h2 class="anchored" data-anchor-id="use-case-2-climate-vulnerability-and-green-roof-modelling">Use Case 2: Climate Vulnerability and Green Roof Modelling</h2>
<p>Informal settlements experience disproportionate climate risks. Green roofs offer a relatively low-cost intervention for heat reduction, but planners need to understand the cost-benefit tradeoffs at different scales.</p>
<p>We model the relationship between green roof coverage and temperature reduction, then estimate the economic implications:</p>
<div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb2-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(ggplot2)</span>
<span id="cb2-2"></span>
<span id="cb2-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Model temperature reduction vs coverage</span></span>
<span id="cb2-4">coverage_model <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tibble</span>(</span>
<span id="cb2-5">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">coverage_pct =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">seq</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">by =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>),</span>
<span id="cb2-6">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">temp_reduction =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> (coverage_pct <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100</span>),  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Simplified linear model</span></span>
<span id="cb2-7">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">avoided_heat_days =</span> temp_reduction <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">8</span>,       <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Days above threshold</span></span>
<span id="cb2-8">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">cost_per_sqm =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">45</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.15</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> coverage_pct      <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Economies of scale</span></span>
<span id="cb2-9">)</span>
<span id="cb2-10"></span>
<span id="cb2-11"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ggplot</span>(coverage_model, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">aes</span>(coverage_pct, temp_reduction)) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb2-12">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">geom_line</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">color =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#14b8a6"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">linewidth =</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.2</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb2-13">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">labs</span>(</span>
<span id="cb2-14">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">title =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Green Roof Coverage vs Temperature Reduction"</span>,</span>
<span id="cb2-15">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">x =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Coverage (%)"</span>,</span>
<span id="cb2-16">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">y =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Temperature Reduction (°C)"</span></span>
<span id="cb2-17">  )</span></code></pre></div>
<p>The analysis typically reveals diminishing cost-efficiency returns at higher coverage percentages — helping planners identify optimal intervention scales that maximise impact per unit of investment.</p>
</section>
<section id="use-case-3-water-resource-optimisation" class="level2">
<h2 class="anchored" data-anchor-id="use-case-3-water-resource-optimisation">Use Case 3: Water Resource Optimisation</h2>
<p>Many settlements experience simultaneous water scarcity during dry periods and flooding during rains. Rainwater harvesting systems can address both, but optimal tank sizing requires statistical modelling of rainfall patterns:</p>
<div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb3-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(forecast)</span>
<span id="cb3-2"></span>
<span id="cb3-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Model rainfall and harvesting potential</span></span>
<span id="cb3-4">rainfall_model <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> rainfall_data <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb3-5">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">group_by</span>(month) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb3-6">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">summarise</span>(</span>
<span id="cb3-7">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">mean_rain =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mean</span>(rainfall_mm),</span>
<span id="cb3-8">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">p25 =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">quantile</span>(rainfall_mm, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.25</span>),</span>
<span id="cb3-9">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">p75 =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">quantile</span>(rainfall_mm, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.75</span>)</span>
<span id="cb3-10">  )</span>
<span id="cb3-11"></span>
<span id="cb3-12"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Optimise tank capacity for reliability</span></span>
<span id="cb3-13">tank_optimisation <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tibble</span>(</span>
<span id="cb3-14">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">capacity_litres =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">seq</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">500</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10000</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">by =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">500</span>)</span>
<span id="cb3-15">) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb3-16">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mutate</span>(</span>
<span id="cb3-17">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">reliability =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">map_dbl</span>(capacity_litres, <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">~</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">simulate_tank</span>(., rainfall_data)),</span>
<span id="cb3-18">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">cost =</span> capacity_litres <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> cost_per_litre</span>
<span id="cb3-19">  )</span></code></pre></div>
</section>
<section id="use-case-4-air-quality-and-vegetation-impact" class="level2">
<h2 class="anchored" data-anchor-id="use-case-4-air-quality-and-vegetation-impact">Use Case 4: Air Quality and Vegetation Impact</h2>
<p>Informal settlements frequently suffer from poor air quality. We use spatial correlation analysis to quantify the relationship between vegetation cover (measured by NDVI from satellite imagery) and particulate matter concentrations:</p>
<div class="sourceCode" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb4-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(terra)</span>
<span id="cb4-2"></span>
<span id="cb4-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Correlate vegetation with air quality</span></span>
<span id="cb4-4">ndvi <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">rast</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"settlement_ndvi.tif"</span>)</span>
<span id="cb4-5">pm25 <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">rast</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"pm25_measurements.tif"</span>)</span>
<span id="cb4-6"></span>
<span id="cb4-7">correlation <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">layerCor</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(ndvi, pm25), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">fun =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"pearson"</span>)</span></code></pre></div>
<p>This analysis consistently shows that increased vegetation significantly reduces particulate pollution — providing an evidence base for prioritising tree planting and green space creation in the most polluted areas.</p>
</section>
<section id="the-broader-vision" class="level2">
<h2 class="anchored" data-anchor-id="the-broader-vision">The Broader Vision</h2>
<p>R enables a level of analytical rigour in informal settlement planning that was previously accessible only to well-resourced formal development projects. By making these tools open and reproducible, we aim to democratise evidence-based environmental planning for the communities that need it most.</p>
<p>This work sits at the intersection of our environmental data science practice and our commitment to using technology for social impact. The same spatial analysis capabilities we use for EIA assessments and biodiversity monitoring translate directly to urban environmental challenges — different domain, same engineering discipline.</p>


</section>

 ]]></description>
  <category>Environmental Data Science</category>
  <category>Urban Planning</category>
  <category>R</category>
  <category>Geospatial</category>
  <guid>https://kwizresearch.com/blog/posts/r-green-architecture-slums/</guid>
  <pubDate>Fri, 07 Nov 2025 00:00:00 GMT</pubDate>
</item>
<item>
  <title>The Complete Guide to Hosting R Shiny Applications</title>
  <dc:creator>Kwizera Jean</dc:creator>
  <link>https://kwizresearch.com/blog/posts/rshiny-hosting-guide/</link>
  <description><![CDATA[ 




<section id="the-deployment-challenge" class="level2">
<h2 class="anchored" data-anchor-id="the-deployment-challenge">The Deployment Challenge</h2>
<p>Creating a brilliant Shiny app is only half the battle. Moving from a working prototype on your laptop to a production application that serves users reliably is where many R developers get stuck. The hosting landscape for Shiny applications has matured significantly, but choosing the right platform requires understanding the tradeoffs between simplicity, cost, control, and scalability.</p>
<p>This guide walks through the major hosting options, helps you choose the right one for your situation, and provides practical deployment guidance.</p>
</section>
<section id="the-hosting-landscape" class="level2">
<h2 class="anchored" data-anchor-id="the-hosting-landscape">The Hosting Landscape</h2>
<section id="shinyapps.io-managed-cloud" class="level3">
<h3 class="anchored" data-anchor-id="shinyapps.io-managed-cloud">1. shinyapps.io — Managed Cloud</h3>
<p><strong>Best for:</strong> Quick prototypes, small teams, minimal DevOps overhead</p>
<p>Posit’s managed hosting platform is the fastest path from development to deployment. Upload your app with a single function call and Posit handles infrastructure, scaling, and SSL.</p>
<div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb1-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(rsconnect)</span>
<span id="cb1-2">rsconnect<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">deployApp</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"my_app/"</span>)</span></code></pre></div>
<p><strong>Free tier:</strong> 5 applications, 25 active hours per month. Sufficient for demos and personal projects, but production applications will need a paid plan.</p>
<p><strong>Limitations:</strong> Limited customisation, shared infrastructure, and costs scale quickly at higher tiers. No control over the server environment.</p>
</section>
<section id="posit-connect-enterprise-platform" class="level3">
<h3 class="anchored" data-anchor-id="posit-connect-enterprise-platform">2. Posit Connect — Enterprise Platform</h3>
<p><strong>Best for:</strong> Organisations running multiple R/Python applications with enterprise security requirements</p>
<p>Posit Connect supports Shiny apps, R Markdown, Plumber APIs, and Python content. It integrates with LDAP/Active Directory for authentication and provides scheduled execution for reports.</p>
<p><strong>Cost:</strong> Approximately $20,000+/year. This is the enterprise option — appropriate when the organisation is standardised on R and needs a managed platform with authentication, access control, and multi-content support.</p>
</section>
<section id="self-hosted-shiny-server-open-source" class="level3">
<h3 class="anchored" data-anchor-id="self-hosted-shiny-server-open-source">3. Self-Hosted Shiny Server (Open Source)</h3>
<p><strong>Best for:</strong> Teams with Linux administration skills who want zero licensing costs</p>
<p>Shiny Server Open Source is free and gives you full control over the environment. The tradeoff is that you manage everything: installation, configuration, updates, security, and monitoring.</p>
<p>Key setup steps include R installation, Shiny Server configuration, Nginx reverse proxy for SSL termination, and authentication (which must be implemented separately, as open-source Shiny Server does not include built-in auth).</p>
</section>
<section id="docker-containerisation" class="level3">
<h3 class="anchored" data-anchor-id="docker-containerisation">4. Docker Containerisation</h3>
<p><strong>Best for:</strong> Reproducible deployments, cloud-native architecture, CI/CD integration</p>
<p>Docker is our recommended approach for production Shiny applications. It provides environment isolation, reproducibility via <code>renv</code>, and portability across cloud providers.</p>
<div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode dockerfile code-with-copy"><code class="sourceCode dockerfile"><span id="cb2-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">FROM</span> rocker/shiny-verse:4.3.0</span>
<span id="cb2-2"></span>
<span id="cb2-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Install system dependencies</span></span>
<span id="cb2-4"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">RUN</span> <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">apt-get</span> update <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">&amp;&amp;</span> <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">apt-get</span> install <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-y</span> libpq-dev</span>
<span id="cb2-5"></span>
<span id="cb2-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Copy renv lockfile and restore packages</span></span>
<span id="cb2-7"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">COPY</span> renv.lock /app/renv.lock</span>
<span id="cb2-8"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">WORKDIR</span> /app</span>
<span id="cb2-9"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">RUN</span> <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">R</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-e</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"renv::restore()"</span></span>
<span id="cb2-10"></span>
<span id="cb2-11"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Copy application code</span></span>
<span id="cb2-12"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">COPY</span> . /app</span>
<span id="cb2-13"></span>
<span id="cb2-14"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">EXPOSE</span> 3838</span>
<span id="cb2-15"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">CMD</span> [<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"R"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"-e"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"shiny::runApp('/app', port = 3838, host = '0.0.0.0')"</span>]</span></code></pre></div>
<p>This approach pairs well with cloud platforms like DigitalOcean, AWS ECS, or Google Cloud Run.</p>
</section>
<section id="cloud-platforms-aws-azure-gcp" class="level3">
<h3 class="anchored" data-anchor-id="cloud-platforms-aws-azure-gcp">5. Cloud Platforms (AWS, Azure, GCP)</h3>
<p><strong>Best for:</strong> Maximum control, enterprise scale, existing cloud infrastructure</p>
<p>Running Shiny on cloud VMs gives you complete control but requires DevOps expertise. AWS EC2, Azure VMs, and Google Compute Engine all work well, typically paired with a Docker container and a load balancer for production use.</p>
</section>
</section>
<section id="decision-framework" class="level2">
<h2 class="anchored" data-anchor-id="decision-framework">Decision Framework</h2>
<table class="table">
<thead>
<tr class="header">
<th>Priority</th>
<th>Recommended Platform</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>Fastest deployment</td>
<td>shinyapps.io</td>
</tr>
<tr class="even">
<td>Lowest cost</td>
<td>Self-hosted open source</td>
</tr>
<tr class="odd">
<td>Enterprise auth &amp; governance</td>
<td>Posit Connect</td>
</tr>
<tr class="even">
<td>Maximum control &amp; reproducibility</td>
<td>Docker + cloud provider</td>
</tr>
</tbody>
</table>
</section>
<section id="essential-production-considerations" class="level2">
<h2 class="anchored" data-anchor-id="essential-production-considerations">Essential Production Considerations</h2>
<section id="authentication" class="level3">
<h3 class="anchored" data-anchor-id="authentication">Authentication</h3>
<p>For apps that need access control, options include:</p>
<ul>
<li><strong>ShinyProxy</strong> — open-source container-based solution that provides authentication and user management</li>
<li><strong>Nginx basic auth</strong> — simple but functional for internal tools</li>
<li><strong>OAuth integration</strong> — for applications requiring Google, GitHub, or enterprise SSO login</li>
</ul>
</section>
<section id="performance-optimisation" class="level3">
<h3 class="anchored" data-anchor-id="performance-optimisation">Performance Optimisation</h3>
<p>Production Shiny apps need careful attention to performance:</p>
<ul>
<li><strong>Caching</strong> — use <code>bindCache()</code> or <code>memoise</code> for expensive computations</li>
<li><strong>Async processing</strong> — <code>promises</code> and <code>future</code> for long-running operations</li>
<li><strong>Database connection pooling</strong> — <code>pool</code> package for efficient database access</li>
<li><strong>Profiling</strong> — <code>profvis</code> to identify bottlenecks before deployment</li>
</ul>
</section>
<section id="monitoring-and-logging" class="level3">
<h3 class="anchored" data-anchor-id="monitoring-and-logging">Monitoring and Logging</h3>
<p>Production applications need observability:</p>
<ul>
<li>Application-level logging with <code>logger</code> or <code>futile.logger</code></li>
<li>Server-level monitoring with uptime checks</li>
<li>Error tracking and alerting</li>
<li>Usage analytics for capacity planning</li>
</ul>
</section>
<section id="cicd-automation" class="level3">
<h3 class="anchored" data-anchor-id="cicd-automation">CI/CD Automation</h3>
<p>Automated deployment removes human error and speeds iteration:</p>
<div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode yaml code-with-copy"><code class="sourceCode yaml"><span id="cb3-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># GitHub Actions: deploy on push to main</span></span>
<span id="cb3-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">name</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> Deploy Shiny App</span></span>
<span id="cb3-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">on</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb3-4"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">push</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb3-5"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">branches</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">[</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">main</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">]</span></span>
<span id="cb3-6"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">jobs</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb3-7"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">deploy</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb3-8"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">runs-on</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> ubuntu-latest</span></span>
<span id="cb3-9"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">steps</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb3-10"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">uses</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> actions/checkout@v4</span></span>
<span id="cb3-11"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">uses</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> r-lib/actions/setup-r@v2</span></span>
<span id="cb3-12"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">run</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> Rscript -e "rsconnect::deployApp('.')"</span></span></code></pre></div>
</section>
</section>
<section id="our-recommendation" class="level2">
<h2 class="anchored" data-anchor-id="our-recommendation">Our Recommendation</h2>
<p>Start with shinyapps.io for prototyping and stakeholder demos. When you need production stability, move to Docker containers deployed on your preferred cloud provider. This progression gives you speed early and control when it matters.</p>
<p>The key principle: <strong>get your app in front of users first, then optimise the infrastructure.</strong> Over-engineering the hosting before validating the application is a common mistake that slows the path to value.</p>


</section>

 ]]></description>
  <category>R</category>
  <category>Shiny</category>
  <category>DevOps</category>
  <category>Enterprise Applications</category>
  <guid>https://kwizresearch.com/blog/posts/rshiny-hosting-guide/</guid>
  <pubDate>Mon, 27 Oct 2025 00:00:00 GMT</pubDate>
</item>
<item>
  <title>Deploying Quarto Websites to the Cloud: A Practical Guide</title>
  <dc:creator>Kwizera Jean</dc:creator>
  <link>https://kwizresearch.com/blog/posts/quarto-cloud-deployment/</link>
  <description><![CDATA[ 




<section id="why-quarto-for-technical-websites" class="level2">
<h2 class="anchored" data-anchor-id="why-quarto-for-technical-websites">Why Quarto for Technical Websites</h2>
<p>Quarto has emerged as the go-to publishing system for data scientists and technical teams. It renders R, Python, Julia, and Observable code into beautiful websites, documents, and presentations — all from plain text source files. But once you’ve built your Quarto site locally, you need to get it online.</p>
<p>This guide covers the major deployment options, from zero-config platforms to enterprise cloud infrastructure.</p>
</section>
<section id="understanding-quarto-outputs" class="level2">
<h2 class="anchored" data-anchor-id="understanding-quarto-outputs">Understanding Quarto Outputs</h2>
<p>Before choosing a platform, understand what you’re deploying:</p>
<ul>
<li><strong>Static websites</strong> (HTML/CSS/JS) — the most common output. These are just files that any web server can host.</li>
<li><strong>Interactive documents</strong> — Quarto docs with embedded Shiny components require a server-side R process. These need specialised hosting.</li>
<li><strong>Non-web outputs</strong> — PDFs, Word documents, and presentations are generated locally and don’t need hosting.</li>
</ul>
<p>Most Quarto projects produce static websites, which gives you the widest range of hosting options.</p>
</section>
<section id="quick-start-platforms" class="level2">
<h2 class="anchored" data-anchor-id="quick-start-platforms">Quick Start Platforms</h2>
<section id="github-pages-free-and-simple" class="level3">
<h3 class="anchored" data-anchor-id="github-pages-free-and-simple">GitHub Pages — Free and Simple</h3>
<p>The most straightforward option for open-source projects and personal sites. GitHub Pages serves static files directly from your repository.</p>
<p><strong>Setup with GitHub Actions:</strong></p>
<div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode yaml code-with-copy"><code class="sourceCode yaml"><span id="cb1-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># .github/workflows/publish.yml</span></span>
<span id="cb1-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">name</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> Render and Deploy Quarto</span></span>
<span id="cb1-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">on</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb1-4"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">push</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb1-5"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">branches</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">[</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">main</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">]</span></span>
<span id="cb1-6"></span>
<span id="cb1-7"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">jobs</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb1-8"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">build-deploy</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb1-9"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">runs-on</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> ubuntu-latest</span></span>
<span id="cb1-10"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">permissions</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb1-11"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">contents</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> write</span></span>
<span id="cb1-12"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">steps</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb1-13"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">uses</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> actions/checkout@v4</span></span>
<span id="cb1-14"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">uses</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> quarto-dev/quarto-actions/setup@v2</span></span>
<span id="cb1-15"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">name</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> Render</span></span>
<span id="cb1-16"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">run</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> quarto render</span></span>
<span id="cb1-17"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">name</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> Deploy</span></span>
<span id="cb1-18"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">uses</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> peaceiris/actions-gh-pages@v4</span></span>
<span id="cb1-19"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">with</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb1-20"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">          </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">github_token</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> ${{ secrets.GITHUB_TOKEN }}</span></span>
<span id="cb1-21"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">          </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">publish_dir</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> ./_site</span></span></code></pre></div>
<p><strong>Pros:</strong> Completely free, unlimited bandwidth, integrated with your repo. <strong>Cons:</strong> Public repos only (for free tier), no server-side processing.</p>
</section>
<section id="netlify-developer-friendly" class="level3">
<h3 class="anchored" data-anchor-id="netlify-developer-friendly">Netlify — Developer-Friendly</h3>
<p>Netlify offers a generous free tier (100 GB bandwidth/month) with excellent developer experience. Connect your repository and Netlify builds and deploys automatically on every push.</p>
<p><strong>Setup:</strong></p>
<ol type="1">
<li>Connect your GitHub/GitLab repository to Netlify</li>
<li>Set build command: <code>quarto render</code></li>
<li>Set publish directory: <code>_site</code></li>
</ol>
<p>Netlify also supports deploy previews for pull requests — invaluable for team collaboration.</p>
</section>
<section id="cloudflare-pages-unlimited-bandwidth" class="level3">
<h3 class="anchored" data-anchor-id="cloudflare-pages-unlimited-bandwidth">Cloudflare Pages — Unlimited Bandwidth</h3>
<p>Cloudflare Pages offers unlimited bandwidth on its free tier, making it an attractive option for high-traffic sites. Setup is similar to Netlify.</p>
</section>
</section>
<section id="enterprise-cloud-deployments" class="level2">
<h2 class="anchored" data-anchor-id="enterprise-cloud-deployments">Enterprise Cloud Deployments</h2>
<section id="aws-s3-cloudfront" class="level3">
<h3 class="anchored" data-anchor-id="aws-s3-cloudfront">AWS: S3 + CloudFront</h3>
<p>For production deployments requiring maximum control, AWS’s combination of S3 (storage) and CloudFront (CDN) is the industry standard.</p>
<div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb2-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Render and sync to S3</span></span>
<span id="cb2-2"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">quarto</span> render</span>
<span id="cb2-3"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">aws</span> s3 sync _site/ s3://your-bucket-name <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">--delete</span></span>
<span id="cb2-4"></span>
<span id="cb2-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Invalidate CloudFront cache</span></span>
<span id="cb2-6"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">aws</span> cloudfront create-invalidation <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">\</span></span>
<span id="cb2-7">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">--distribution-id</span> YOUR_DIST_ID <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">\</span></span>
<span id="cb2-8">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">--paths</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"/*"</span></span></code></pre></div>
<p><strong>Cost:</strong> Typically under $5/month for moderate-traffic sites. S3 storage is pennies; CloudFront charges per request.</p>
</section>
<section id="google-cloud-firebase-hosting" class="level3">
<h3 class="anchored" data-anchor-id="google-cloud-firebase-hosting">Google Cloud: Firebase Hosting</h3>
<p>Firebase Hosting is Google’s easiest path for static sites. It provides a global CDN, automatic SSL, and simple CLI deployment.</p>
<div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb3-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">firebase</span> init hosting</span>
<span id="cb3-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Set public directory to _site</span></span>
<span id="cb3-3"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">quarto</span> render</span>
<span id="cb3-4"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">firebase</span> deploy</span></code></pre></div>
</section>
<section id="azure-static-web-apps" class="level3">
<h3 class="anchored" data-anchor-id="azure-static-web-apps">Azure: Static Web Apps</h3>
<p>Azure Static Web Apps provides free hosting with GitHub Actions integration, custom domains, and authentication support.</p>
</section>
</section>
<section id="cicd-best-practices" class="level2">
<h2 class="anchored" data-anchor-id="cicd-best-practices">CI/CD Best Practices</h2>
<p>Automate your deployment pipeline from day one. Manual deployment introduces human error and slows iteration.</p>
<p><strong>Key principles:</strong></p>
<ol type="1">
<li><strong>Render on CI, not locally.</strong> This ensures your site builds from a clean environment, catching dependency issues early.</li>
<li><strong>Pin your Quarto version.</strong> Use <code>quarto-dev/quarto-actions/setup@v2</code> with a specific version to avoid surprise breaking changes.</li>
<li><strong>Cache dependencies.</strong> R and Python package installation is the slowest part of most builds. Cache aggressively.</li>
<li><strong>Deploy previews for PRs.</strong> Netlify and Cloudflare Pages offer this out of the box. For other platforms, deploy to a staging URL on pull requests.</li>
</ol>
</section>
<section id="performance-optimisation" class="level2">
<h2 class="anchored" data-anchor-id="performance-optimisation">Performance Optimisation</h2>
<p>Once deployed, optimise for speed:</p>
<ul>
<li><strong>Image optimisation</strong> — Use modern formats (WebP, AVIF) and appropriate dimensions. Tools like <code>sharp</code> or <code>imagemagick</code> can automate this in your build pipeline.</li>
<li><strong>Asset minification</strong> — Quarto handles CSS/JS minification by default, but verify this in your <code>_quarto.yml</code> configuration.</li>
<li><strong>Caching headers</strong> — Configure your CDN to cache static assets aggressively (e.g., 1 year for hashed filenames).</li>
<li><strong>Compression</strong> — Enable gzip or Brotli compression at the server/CDN level.</li>
</ul>
</section>
<section id="choosing-the-right-platform" class="level2">
<h2 class="anchored" data-anchor-id="choosing-the-right-platform">Choosing the Right Platform</h2>
<table class="table">
<thead>
<tr class="header">
<th>Need</th>
<th>Platform</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>Simplest setup, open source</td>
<td>GitHub Pages</td>
</tr>
<tr class="even">
<td>Best developer experience</td>
<td>Netlify</td>
</tr>
<tr class="odd">
<td>Unlimited free bandwidth</td>
<td>Cloudflare Pages</td>
</tr>
<tr class="even">
<td>Maximum control, enterprise</td>
<td>AWS S3 + CloudFront</td>
</tr>
<tr class="odd">
<td>Google ecosystem</td>
<td>Firebase Hosting</td>
</tr>
<tr class="even">
<td>Microsoft ecosystem</td>
<td>Azure Static Web Apps</td>
</tr>
</tbody>
</table>
</section>
<section id="our-approach" class="level2">
<h2 class="anchored" data-anchor-id="our-approach">Our Approach</h2>
<p>At Kwiz Computing, we use GitHub Pages for our public-facing Quarto sites (including this one) and AWS infrastructure for client projects requiring custom domains, access control, or integration with existing cloud environments.</p>
<p>The fundamental recommendation: <strong>deploy early, automate immediately.</strong> Getting your content online and iterating in production is more valuable than perfecting your local build. Start with a free tier platform, add CI/CD automation, and only move to enterprise infrastructure when you have a concrete reason.</p>


</section>

 ]]></description>
  <category>Quarto</category>
  <category>DevOps</category>
  <category>Cloud</category>
  <category>Web Development</category>
  <guid>https://kwizresearch.com/blog/posts/quarto-cloud-deployment/</guid>
  <pubDate>Mon, 20 Oct 2025 00:00:00 GMT</pubDate>
</item>
</channel>
</rss>
