<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Sarah-Anne Ndlovu</title>
    <description>The latest articles on DEV Community by Sarah-Anne Ndlovu (@awenroswer2443dot).</description>
    <link>https://dev.to/awenroswer2443dot</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3975780%2F734b25ea-7c1d-4818-99c1-b929e703c4f2.png</url>
      <title>DEV Community: Sarah-Anne Ndlovu</title>
      <link>https://dev.to/awenroswer2443dot</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/awenroswer2443dot"/>
    <language>en</language>
    <item>
      <title>Building DiamondScope: reverse-engineering South African diamond pricing in TypeScript</title>
      <dc:creator>Sarah-Anne Ndlovu</dc:creator>
      <pubDate>Tue, 09 Jun 2026 10:47:28 +0000</pubDate>
      <link>https://dev.to/awenroswer2443dot/building-diamondscope-reverse-engineering-south-african-diamond-pricing-in-typescript-ag5</link>
      <guid>https://dev.to/awenroswer2443dot/building-diamondscope-reverse-engineering-south-african-diamond-pricing-in-typescript-ag5</guid>
      <description>&lt;p&gt;The Rapaport wholesale diamond price list is paywalled and trade-only. South African retail buyers cannot translate any quote they receive to its underlying wholesale cost. The result: chain retail jewellers can mark stones up 100 to 150 percent over the manufacturer-direct price and the buyer has no way to spot it.&lt;/p&gt;

&lt;p&gt;I just shipped a free open-source tool that fixes that asymmetry. It is at &lt;a href="https://naturaldiamond.co.za/diamondscope/" rel="noopener noreferrer"&gt;naturaldiamond.co.za/diamondscope/&lt;/a&gt; and the engine is on GitHub at &lt;a href="https://github.com/awenroswer2443-dot/diamondscope-engine" rel="noopener noreferrer"&gt;awenroswer2443-dot/diamondscope-engine&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This post is a quick technical writeup on the engine design.&lt;/p&gt;

&lt;h2&gt;
  
  
  The thesis
&lt;/h2&gt;

&lt;p&gt;Most diamond pricing tools are: input filter, then show inventory. DiamondScope flips that: input a chain-retail price, output the spec inferred behind it and the channel-spread breakdown.&lt;/p&gt;

&lt;p&gt;Given a spec (shape, carat, colour, clarity, cut) the engine returns:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The Rapaport-equivalent ZAR baseline.&lt;/li&gt;
&lt;li&gt;The expected price across manufacturer-direct, boutique retail, and chain retail.&lt;/li&gt;
&lt;li&gt;Per-retailer quotes for the major South African jewellers.&lt;/li&gt;
&lt;li&gt;Adjacent-spec deltas (drop one colour grade, save R7,400).&lt;/li&gt;
&lt;li&gt;Editorial commentary.&lt;/li&gt;
&lt;li&gt;A confidence indicator for the baseline.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It runs entirely client-side. No backend.&lt;/p&gt;

&lt;h2&gt;
  
  
  The math
&lt;/h2&gt;

&lt;p&gt;The core baseline is a single per-stone Rapaport-equivalent ZAR value for a 1.00 ct round brilliant, G colour, SI1 clarity, GIA Excellent cut. All other specs scale from there.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;RAPAPORT_BASELINE_ZAR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="nx"&gt;_000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;rapaportZAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DiamondSpec&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;caratCoeff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;interpolateCaratCoefficient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;carat&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;colourMult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;COLOUR_MULTIPLIER&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;colour&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;clarityMult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;CLARITY_MULTIPLIER&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clarity&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cutMult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;CUT_MULTIPLIER&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cut&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;shapeMult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;SHAPE_MULTIPLIER&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;RAPAPORT_BASELINE_ZAR&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;caratCoeff&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;colourMult&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;clarityMult&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;cutMult&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;shapeMult&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Carat coefficient anchors are calibrated from published per-spec ranges on &lt;a href="https://naturaldiamond.co.za/1-carat-diamond-price-south-africa/" rel="noopener noreferrer"&gt;naturaldiamond.co.za/1-carat-diamond-price-south-africa/&lt;/a&gt;, the 2-carat page, and the 3-carat page. Between anchors, linear interpolation.&lt;/p&gt;

&lt;p&gt;Colour, clarity, cut, and shape multipliers are calibrated against the same published ranges, then validated against a hand-checked sample.&lt;/p&gt;

&lt;h2&gt;
  
  
  Channel premiums
&lt;/h2&gt;

&lt;p&gt;The three channels each get a multiplier:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Manufacturer-direct: 0.55 to 0.75 of Rap (mid 0.65). RAP-minus 25 to 45 percent.&lt;/li&gt;
&lt;li&gt;Boutique retail: 0.80 to 1.05 of Rap (mid 0.92). RAP-minus 5 to 20 percent.&lt;/li&gt;
&lt;li&gt;Chain retail: 0.55 to 1.20 of Rap (mid 0.85). Highly variable.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Chain retail variance is structural. Chain retailers often drop the cut grade from Excellent to Very Good at the same nominal price point, which compresses the headline price toward the manufacturer-direct number even though the stone is quietly worse.&lt;/p&gt;

&lt;h2&gt;
  
  
  Per-retailer premiums
&lt;/h2&gt;

&lt;p&gt;The engine ships with nine calibrated South African retailer entries (Prodiam, Nungu, Jack Friedman, Charles Greig, Shimansky, Browns, American Swiss, Sterns, NWJ). Each has an &lt;code&gt;ofRapaport&lt;/code&gt; multiplier and channel classification. The data is in &lt;code&gt;SA_RETAILER_PREMIUMS&lt;/code&gt; and gets refreshed quarterly.&lt;/p&gt;

&lt;h2&gt;
  
  
  URL state encoding
&lt;/h2&gt;

&lt;p&gt;Every DiamondScope result is shareable. The spec is encoded in URL query params so any link regenerates the same result on the recipient's device. The page hydrates from URL on load:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URLSearchParams&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fromQuery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseSpecFromQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fromQuery&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;spec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fromQuery&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why TypeScript
&lt;/h2&gt;

&lt;p&gt;The pricing data is highly enum-shaped (4 colour grades, 9 clarity grades, 4 cut grades, 10 shapes). TypeScript's literal-type unions catch every grade lookup miss at compile time. The data files essentially document themselves through their type definitions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Source
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Live tool: &lt;a href="https://naturaldiamond.co.za/diamondscope/" rel="noopener noreferrer"&gt;naturaldiamond.co.za/diamondscope/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Open-source engine: &lt;a href="https://github.com/awenroswer2443-dot/diamondscope-engine" rel="noopener noreferrer"&gt;github.com/awenroswer2443-dot/diamondscope-engine&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Methodology docs: &lt;a href="https://github.com/awenroswer2443-dot/diamondscope-engine/blob/main/docs/methodology.md" rel="noopener noreferrer"&gt;the methodology.md in the repo&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Editorial publication: &lt;a href="https://naturaldiamond.co.za" rel="noopener noreferrer"&gt;naturaldiamond.co.za&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Feedback welcome. Especially on the calibration data; if you have ground-truth manufacturer-direct quotes that differ from the engine's estimates, open an issue with the spec and the actual quote and I will tune the coefficients.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>opensource</category>
      <category>calculator</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
