<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Juan Torres</title>
    <link>https://juantorresmi.github.io</link>
    <description>Juan Torres studies Logistics Management at Central Michigan University, works at Meijer and builds projects about routing, simulation and pricing.</description>
    <language>en</language>
    <lastBuildDate>Thu, 29 Oct 2026 22:10:09 -0400</lastBuildDate>
    <managingEditor>Torre10j@cmich.edu (Juan Torres)</managingEditor>
    <image><url>https://juantorresmi.github.io/assets/icon-512.png</url><title>Juan Torres</title><link>https://juantorresmi.github.io</link></image>
    <atom:link href="https://juantorresmi.github.io/feed.xml" rel="self" type="application/rss+xml"/>
    <item>
      <title>A pork price board from USDA&apos;s daily report, every cut in plain English and Spanish</title>
      <link>https://juantorresmi.github.io/blog/usda-pork-prices-by-cut/</link>
      <guid isPermaLink="true">https://juantorresmi.github.io/blog/usda-pork-prices-by-cut/</guid>
      <pubDate>Sat, 17 Oct 2026 00:00:00 -0400</pubDate>
      <description>How the site&apos;s pork board turns USDA&apos;s LM_PK602 report into a page for every pork cut, in English and Spanish, and what six years of the pork cutout show.</description>
      <content:encoded><![CDATA[<p>What does a boneless pork butt cost by the box today? USDA answers that every business day afternoon in its pork report, under a name like <code class="language-plaintext highlighter-rouge">1/4 Trim Bnls Butt VAC</code>. The <a href="/pork-prices/">Pork Price Board</a> is the same kind of board as the <a href="/beef-prices/">boxed beef board</a>: the day’s cutout and primals, every cut that traded, a page for each cut with its history since 2020, and every number as a free download. It also has a <a href="/es/precios-de-cerdo/">Spanish version</a>, page for page.</p>

<h2 id="the-data">The data</h2>

<p>The report is USDA’s <a href="https://mymarketnews.ams.usda.gov/viewReport/2498">National Daily Pork FOB Plant, Negotiated Sales, afternoon edition</a> (LM_PK602). Packers report their sales under Livestock Mandatory Reporting, and USDA publishes:</p>

<ul>
  <li><strong>The carcass cutout and six primals</strong> (loin, butt, picnic, rib, ham and belly), in dollars per hundredweight.</li>
  <li><strong>One row per item</strong>, in ten tables from loin cuts to variety meats and products with added ingredients: the pounds sold, the low, the high and the weighted average. Unlike beef, pork has no grades, so each item has one price a day.</li>
</ul>

<p>Since January 2020 the report has listed 270 different items. Their names are the trade’s shorthand: <code class="language-plaintext highlighter-rouge">Bnls CC Strap-off</code>, <code class="language-plaintext highlighter-rouge">Derind Belly 13-17#</code>, <code class="language-plaintext highlighter-rouge">Trmd Sparerib - MED, 1 PC VAC, FZN</code>.</p>

<h2 id="how-it-works">How it works</h2>

<p>The board reads its history the way the beef board does. USDA’s datamart serves any date range with no key, so the site keeps no copy of its own: a GitHub Action reads back the CSVs it has already published and asks USDA only for the last month or two. The first run took all 82 months since January 2020.</p>

<p>The names were the new part. Instead of writing 270 names by hand, the snapshot splits each description into what the item is and how it’s sold: the bone, the trim (1/4 or 1/8 inch), the weight range, the lean percentage, the pack (vacuum-packed, boxed, a combo bin), frozen, and added ingredients. Each piece has an English and a Spanish word. <code class="language-plaintext highlighter-rouge">1/4 Trim Bnls Butt VAC</code> becomes “Boneless pork butt, 1/4-inch trim, vacuum-packed”, and in Spanish “Espaldilla de cerdo, sin hueso, recorte de 1/4 de pulgada, al vacío”. An item USDA adds later gets named the same way and keeps its address. Any item that traded on at least 26 days in the past year gets a page, which today is 156 of them, plus the cutout and each primal.</p>

<p>The site’s Cloudflare Worker checks USDA every half hour for a new report and keeps only the newest, so a page whose copy is behind can show the afternoon’s figures before the evening rebuild.</p>

<h2 id="what-it-shows">What it shows</h2>

<p>The report for Friday, October 16, 2026 had the pork carcass cutout at $80.55 per hundredweight, up $3.00 from the day before, on 273 loads. A year earlier it was $102.17, so the cutout was down about 21%. The record covers 1,731 business days:</p>

<table>
  <thead>
    <tr>
      <th>Year</th>
      <th>Average cutout</th>
      <th>Low</th>
      <th>High</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>2020</td>
      <td>$77.11</td>
      <td>$51.07 (Apr 9)</td>
      <td>$121.66 (May 11)</td>
    </tr>
    <tr>
      <td>2021</td>
      <td>$104.28</td>
      <td>$77.42</td>
      <td>$134.94 (Jun 8, the record)</td>
    </tr>
    <tr>
      <td>2022</td>
      <td>$103.78</td>
      <td>$81.62</td>
      <td>$131.02</td>
    </tr>
    <tr>
      <td>2023</td>
      <td>$89.84</td>
      <td>$76.02</td>
      <td>$117.21</td>
    </tr>
    <tr>
      <td>2024</td>
      <td>$95.73</td>
      <td>$82.78</td>
      <td>$106.78</td>
    </tr>
    <tr>
      <td>2025</td>
      <td>$102.84</td>
      <td>$87.83</td>
      <td>$123.11</td>
    </tr>
    <tr>
      <td>2026, through October 16</td>
      <td>$95.77</td>
      <td>$77.55 (Oct 15)</td>
      <td>$104.82</td>
    </tr>
  </tbody>
</table>

<p>Pork moves more with the seasons than beef. Measured against each year’s own average (the median over 2020 to 2025), the cutout usually runs highest in July, 13.5% above its year, and lowest in January, 10.6% below. The beef cutout, measured the same way, swings from 5.0% above in June to 9.5% below in February. Single cuts swing harder: derind bellies, the raw material for bacon, usually run 17.1% above their year in August and 18.3% below in December.</p>

<p>October 2026 is near the bottom of that cycle and of the year. Per pound, October 16, 2025 against October 16, 2026:</p>

<table>
  <thead>
    <tr>
      <th>Cut</th>
      <th>A year earlier</th>
      <th>Now</th>
      <th>Change</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><a href="/pork-prices/derind-pork-belly-13-17-lb/">Derind pork belly, 13–17 lb</a></td>
      <td>$1.62</td>
      <td>$1.10</td>
      <td>−32.2%</td>
    </tr>
    <tr>
      <td><a href="/pork-prices/72-lean-pork-trim-combo-bin/">72% lean pork trim, combo bin</a></td>
      <td>$1.13</td>
      <td>$0.76</td>
      <td>−32.8%</td>
    </tr>
    <tr>
      <td><a href="/pork-prices/boneless-center-cut-pork-loin-strap-off/">Boneless center-cut pork loin, strap off</a></td>
      <td>$1.67</td>
      <td>$1.42</td>
      <td>−14.7%</td>
    </tr>
    <tr>
      <td><a href="/pork-prices/boneless-pork-butt-1-4-inch-trim-vacuum-packed/">Boneless pork butt, 1/4-inch trim</a></td>
      <td>$1.47</td>
      <td>$1.36</td>
      <td>−7.4%</td>
    </tr>
    <tr>
      <td><a href="/pork-prices/pork-tenderloin/">Pork tenderloin</a></td>
      <td>$1.78</td>
      <td>$1.73</td>
      <td>−2.5%</td>
    </tr>
  </tbody>
</table>

<p>The back ribs’ $2.44 a pound was higher than on only 1 of the other 253 days they traded in the past year.</p>

<h2 id="where-it-falls-short">Where it falls short</h2>

<ul>
  <li><strong>Thin items are noisy.</strong> Some items trade a few thousand pounds on a day; one deal can move their average a long way. The board’s “biggest moves” list only counts items with at least 20,000 pounds sold that day, and each page shows the pounds behind every price.</li>
  <li><strong>Machine-built names are plain, not perfect.</strong> “Pork picnic, smoker trim (SS)” keeps USDA’s SS because the report doesn’t say what it stands for. The exact USDA description is on every page.</li>
  <li><strong>The Spanish is mine.</strong> The cut names follow what’s used in Mexico, with USDA’s description beside them, but a butcher in another country may know a cut by a different name.</li>
</ul>

<p>The board is at <a href="/pork-prices/">/pork-prices/</a>, in Spanish at <a href="/es/precios-de-cerdo/">/es/precios-de-cerdo/</a>, and with the other USDA boards at <a href="/usda-market-prices/">USDA market prices</a>.</p>
]]></content:encoded>
      <category>logistics</category>
      <category>pork</category>
      <category>usda</category>
      <category>data</category>
    </item>
    <item>
      <title>What packers pay for cattle, region by region, from six USDA reports</title>
      <link>https://juantorresmi.github.io/blog/usda-fed-cattle-prices-by-region/</link>
      <guid isPermaLink="true">https://juantorresmi.github.io/blog/usda-fed-cattle-prices-by-region/</guid>
      <pubDate>Sat, 17 Oct 2026 00:00:00 -0400</pubDate>
      <description>How the site&apos;s fed cattle board puts USDA&apos;s 5-area, state and regional cash cattle reports on one page, what six years of prices show, and where data is thin.</description>
      <content:encoded><![CDATA[<p>What did a packer pay for a pen of fed steers last week, and is that more in Nebraska than in Michigan? USDA publishes the answer every week, but split across half a dozen reports. The <a href="/fed-cattle-prices/">Fed Cattle Price Board</a> puts them on one page: the 5-area price, every region side by side, a page per region with every week since 2020, and a calculator for whether a pen pays more sold live or in the beef. Like the <a href="/beef-prices/">beef</a> and <a href="/pork-prices/">pork</a> boards, it has a <a href="/es/precios-de-ganado-gordo/">Spanish version</a>.</p>

<h2 id="the-data">The data</h2>

<p>All of it comes from Livestock Mandatory Reporting, through the same keyless datamart as the beef and pork boards:</p>

<table>
  <thead>
    <tr>
      <th>Report</th>
      <th>What it holds</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><a href="https://mymarketnews.ams.usda.gov/viewReport/2477">LM_CT150</a></td>
      <td>The 5-area weekly weighted average, by class (steers, heifers, mixed), selling basis and grade</td>
    </tr>
    <tr>
      <td>LM_CT156, 157, 158</td>
      <td>The same detail for Texas-Oklahoma, Kansas and Nebraska, with dressing percentages, cash and grid base</td>
    </tr>
    <tr>
      <td><a href="https://mymarketnews.ams.usda.gov/viewReport/2481">LM_CT154</a></td>
      <td>The national weekly, whose regional table covers Colorado, the Western and Eastern Cornbelt, the northern plains, the West and the Northeast</td>
    </tr>
    <tr>
      <td><a href="https://mymarketnews.ams.usda.gov/viewReport/2466">LM_CT100</a></td>
      <td>The 5-area daily, for the days of the current week</td>
    </tr>
  </tbody>
</table>

<p>Each price comes on four selling bases: live or dressed, and FOB the feedlot or delivered to the plant. The board leads with the two the trade quotes most, live FOB and dressed delivered, and keeps the other two on each region’s page and in the CSV.</p>

<h2 id="how-it-works">How it works</h2>

<p>The snapshot keeps no history of its own. It reads back the board’s CSVs, asks USDA for the current year of each weekly report and the last month or two of the daily, and rebuilds the board and the region pages twice a day. The first run made 152 requests to cover 2020 to now. A weekly report dated on a Monday covers the week before it, so the pages label each week by its Monday to Sunday.</p>

<p>A region gets a page once it has a price in at least 8 weeks of the past year. Eight do today. Colorado (last cash price in December 2022) and the Western States (September 2025) are in the table and the CSV but don’t have pages yet.</p>

<h2 id="what-it-shows">What it shows</h2>

<p>In the week of October 5 to 11, 2026, 5-area live steers brought $220.14 per hundredweight and dressed steers $345.66, on 47,079 head of steers and heifers bought for cash. The record covers 354 weeks. Live steers, 5-area, weighted by head:</p>

<table>
  <thead>
    <tr>
      <th>Year</th>
      <th>Average</th>
      <th>Low week</th>
      <th>High week</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>2020</td>
      <td>$108.14</td>
      <td>$94.87</td>
      <td>$124.47</td>
    </tr>
    <tr>
      <td>2021</td>
      <td>$122.57</td>
      <td>$109.23</td>
      <td>$140.44</td>
    </tr>
    <tr>
      <td>2022</td>
      <td>$144.24</td>
      <td>$136.61</td>
      <td>$156.42</td>
    </tr>
    <tr>
      <td>2023</td>
      <td>$175.91</td>
      <td>$155.25</td>
      <td>$188.75</td>
    </tr>
    <tr>
      <td>2024</td>
      <td>$186.99</td>
      <td>$172.24</td>
      <td>$197.09</td>
    </tr>
    <tr>
      <td>2025</td>
      <td>$223.82</td>
      <td>$197.65</td>
      <td>$244.25</td>
    </tr>
    <tr>
      <td>2026, through the week of Oct 5</td>
      <td>$241.36</td>
      <td>$219.06</td>
      <td>$262.85 (week of May 11, the record)</td>
    </tr>
  </tbody>
</table>

<p>The price more than doubled in six years. That is why the board measures seasons against each year’s own average: live steers usually run 2.3% above their year in July and 8.0% below in January.</p>

<p>The regions track each other closely. Over the past 53 weeks, all steers and heifers live FOB averaged within 32 cents of the 5-area price in Nebraska, the Western Cornbelt and the Eastern Cornbelt, which is Michigan’s region. The <a href="/fed-cattle-prices/eastern-cornbelt/">Eastern Cornbelt</a> is a small market, though: 2,173 head that week against 23,858 in <a href="/fed-cattle-prices/nebraska/">Nebraska</a>.</p>

<h2 id="where-the-data-is-thin">Where the data is thin</h2>

<ul>
  <li><strong>Texas-Oklahoma and Kansas often have no cash price.</strong> USDA publishes none when there are too few cash trades, and those two regions had a published price in only 31 and 45 of the past 52 weeks. Their pages say which week the last price is from instead of showing a stale number as current.</li>
  <li><strong>The daily report is lumpy.</strong> Cash buying bunches into a day or two a week, so most daily rows are small. The weekly is the price to quote.</li>
  <li><strong>The calculator compares prices, not bids.</strong> Live is FOB the feedlot and dressed is delivered, so freight and shrink sit on different sides. It shows the dressing percentage where the two pay the same, which is the useful number.</li>
</ul>

<p>The board is at <a href="/fed-cattle-prices/">/fed-cattle-prices/</a>, in Spanish at <a href="/es/precios-de-ganado-gordo/">/es/precios-de-ganado-gordo/</a>, and with the other USDA boards at <a href="/usda-market-prices/">USDA market prices</a>.</p>
]]></content:encoded>
      <category>logistics</category>
      <category>cattle</category>
      <category>usda</category>
      <category>data</category>
    </item>
    <item>
      <title>I put my YouTube channel&apos;s live numbers on my own site</title>
      <link>https://juantorresmi.github.io/blog/youtube-channel-stats-on-my-site/</link>
      <guid isPermaLink="true">https://juantorresmi.github.io/blog/youtube-channel-stats-on-my-site/</guid>
      <pubDate>Fri, 16 Oct 2026 00:00:00 -0400</pubDate>
      <description>Why I put my YouTube channel&apos;s stats on this site with a free Cloudflare Worker, how it reads YouTube&apos;s API without exposing a key, and what the numbers show.</description>
      <content:encoded><![CDATA[<p>My YouTube channel is League of Legends: full games and short clips from my Kalista ADC games in Emerald. I already post to it with a Discord bot. The <a href="/youtube/">YouTube page</a> on this site shows the channel’s numbers, refreshed every three hours, and the latest uploads.</p>

<p>I had three reasons for building it. I wanted to build something that works with the Discord bot I already use for posting: the bot puts the videos up, and this page shows how they do. I wanted the channel to be easier to discover. And I wanted somewhere on my own website to track how the channel performs, so the numbers show how my tool is doing.</p>

<h2 id="why-a-page-cant-just-ask-youtube">Why a page can’t just ask YouTube</h2>

<p>YouTube’s numbers come from the YouTube Data API, and every request needs an API key. A web page that asked YouTube directly would have to put that key in its source, where anyone could copy it and spend its quota. So the asking happens somewhere else: a small Cloudflare Worker, the same one behind this site’s <a href="/reefer-rates/">reefer rate board</a> and <a href="/border-waits/">border wait board</a>.</p>

<h2 id="how-it-works">How it works</h2>

<ol>
  <li><strong>Every three hours, the Worker wakes itself up.</strong> A cron trigger runs it at 17 past midnight, 3, 6 and so on (UTC). Nobody has to visit the page, and my computer doesn’t have to be on.</li>
  <li><strong>It asks YouTube three questions.</strong> One call gets the channel’s subscribers, total views and video count, one gets the list of its latest uploads, and one gets each upload’s views, likes and length. Each call costs one unit of YouTube’s free daily quota of 10,000, so the whole day costs 24.</li>
  <li><strong>It writes the answer down.</strong> The latest snapshot goes into a small SQLite database on Cloudflare (D1), along with one row per day for the last 40 days, which is where the day-by-day changes come from. If YouTube doesn’t answer, nothing is overwritten.</li>
  <li><strong>The page reads the snapshot, not YouTube.</strong> A visit costs YouTube’s quota nothing, and Cloudflare keeps a copy at the edge for five minutes, so most visits don’t even reach the database.</li>
  <li><strong>At least once a day, a copy goes into the page itself.</strong> A GitHub Action writes the snapshot into the page’s HTML. That’s what search engines and link previews see. The same run adds each day’s numbers to a <a href="/youtube/youtube-channel-daily.csv">CSV</a> in this site’s repository, which is the permanent record; the database only keeps the recent days the page needs.</li>
</ol>

<p>The key is a plain API key stored as a Cloudflare secret. It can only read public data, so it can’t upload or change anything on the channel. The bot does the uploading with its own sign-in and its own quota, so this page can never get in its way. All of it runs on free plans.</p>

<p>For discoverability, the page is built to be found and to send people on. Its title and description say what the channel is, the numbers and the latest full game and Shorts are in the HTML, every recent upload links to YouTube, and a “Browse by matchup” row turns titles like “Kalista vs Smolder” into searches of the channel.</p>

<h2 id="what-the-numbers-show">What the numbers show</h2>

<p>When the site last copied the snapshot, on October 16, 2026, the channel had 183 videos, 92,443 views all time and 14 subscribers. The 12 latest uploads, from October 13 to 16, split very unevenly:</p>

<table>
  <thead>
    <tr>
      <th>Upload</th>
      <th>Count</th>
      <th>Length</th>
      <th>Views by October 16</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Shorts</td>
      <td>8</td>
      <td>35–43 seconds</td>
      <td>5,838</td>
    </tr>
    <tr>
      <td>Full games</td>
      <td>4</td>
      <td>23–34 minutes</td>
      <td>1</td>
    </tr>
  </tbody>
</table>

<p>The most-viewed was a Short, “What should Thresh &amp; Jhin have done vs Kalista?”, at 1,339 views. The day-by-day log shows the channel gained 2,310 views on October 15 and 2,213 on October 16 by the time of the copy, with no change in subscribers either day.</p>

<h2 id="where-it-breaks">Where it breaks</h2>

<ul>
  <li><strong>It’s a few days of data.</strong> The full games were all uploaded in those same four days, the last one hours before the copy, so their counts may still move. Four days can’t say how a full game does over a month.</li>
  <li><strong>The day-by-day history is short.</strong> It holds one row per day, so it only says something once there are weeks of rows.</li>
  <li><strong>Some of it is YouTube’s rules, not mine.</strong> The public subscriber count is rounded to three significant figures, and “Short” on the page means an upload three minutes long or less, which is YouTube’s own limit for one.</li>
</ul>

<p><em>Updated October 18, 2026: the Worker now polls every three hours instead of every hour, since a channel this size barely moves in an hour, and every day’s numbers are kept in the site’s repository rather than in the Worker’s database. The numbers above are from the October 16 copy.</em></p>

<p>The <a href="/youtube/">YouTube page</a> has the live numbers, the latest full game and Shorts, every recent upload and the day-by-day changes. The Worker’s code is in the <a href="https://github.com/JuanTorresMI/JuanTorresMI.github.io/tree/main/worker">worker folder</a> of this site’s repository.</p>
]]></content:encoded>
      <category>youtube</category>
      <category>cloudflare</category>
      <category>discord</category>
      <category>league-of-legends</category>
    </item>
    <item>
      <title>I turned USDA&apos;s weekly truck rate report into a reefer rate board</title>
      <link>https://juantorresmi.github.io/blog/usda-reefer-truck-rate-board/</link>
      <guid isPermaLink="true">https://juantorresmi.github.io/blog/usda-reefer-truck-rate-board/</guid>
      <pubDate>Fri, 16 Oct 2026 00:00:00 -0400</pubDate>
      <description>How I built a weekly reefer truck rate board from USDA&apos;s truck rate report with a Cloudflare Worker, the typos I found in USDA&apos;s data, and what diesel did.</description>
      <content:encoded><![CDATA[<p>What does it cost to move a refrigerated truckload of produce from South Texas to Chicago this week? The U.S. Department of Agriculture answers that every Wednesday, for dozens of lanes at once. The answer just comes as a PDF, or as thousands of raw rows through an API.</p>

<p>I built the <a href="/reefer-rates/">Reefer Rate Board</a> for two reasons. I wanted to learn more about Cloudflare and APIs: how to call one on a schedule, keep what it returns and serve it back without a server of my own. And I wanted to improve this site’s SEO. A board like this gives search engines a lot to find: a page for every lane, with every number in the HTML.</p>

<h2 id="the-data">The data</h2>

<p>USDA’s Agricultural Marketing Service publishes the <a href="https://mymarketnews.ams.usda.gov/viewReport/2375">Specialty Crops National Truck Rate Report</a> (report 2375) every week. Its market news reporters ask shippers, receivers and truck brokers what it cost to move produce in a 48 to 53 foot refrigerated trailer, broker fees included. Each rate goes from a shipping area, like South Texas, the Imperial Valley or Central Wisconsin, to one of ten big cities. Each one is a range, low to high, sometimes with a “mostly” range where most loads actually moved, plus USDA’s read on whether trucks are short or in surplus.</p>

<p>Through the MyMarketNews API it comes as one row per shipping area, city and commodity. The same rate is repeated for every crop it covers, so one number can show up dozens of times.</p>

<h2 id="how-it-works">How it works</h2>

<ol>
  <li><strong>A Cloudflare Worker asks USDA.</strong> It holds the API key, which is free, read-only, and stored as a Cloudflare secret, never in the site’s code. It fetches whichever week it’s asked for, and the U.S. diesel price from the Energy Information Administration. It stores none of it: USDA and EIA already keep the history.</li>
  <li><strong>It boils the rows down to lanes.</strong> Each distinct rate a shipping area quotes to a city comes back once, with the crops it covered. Usually there’s one rate per lane. Sometimes an area quotes a second rate for a group of crops, citrus most often, and the board lists that separately.</li>
  <li><strong>Twice a day, the site rebuilds.</strong> A GitHub Action reads back the CSV of every week on record and asks the Worker only for the newest week and the four before it, in case USDA corrected one. Then it writes the board, a page for every lane and the CSV. The pages are plain HTML, so reading them never touches the Worker, however many people visit.</li>
</ol>

<p>The free plan shaped the design. A request gets 10 milliseconds of CPU, so the Worker answers one week at a time, and the CSV is written by the GitHub Action as a static file instead of by the Worker. The whole thing costs nothing to run.</p>

<h2 id="what-it-shows">What it shows</h2>

<p>The report for the week of October 12, 2026, published October 14, quoted 68 lanes from 10 shipping areas. Areas out of season keep their pages, so there are 299 lane pages in all, and the CSV holds 30,908 weekly rates back to April 2021. The biggest moves that week:</p>

<table>
  <thead>
    <tr>
      <th>Lane</th>
      <th>Rate per load</th>
      <th>Change</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>South Texas to Atlanta</td>
      <td>$4,200–4,700</td>
      <td>+14%</td>
    </tr>
    <tr>
      <td>Imperial Valley &amp; Yuma to Chicago</td>
      <td>$7,280–7,705</td>
      <td>+13%</td>
    </tr>
    <tr>
      <td>Santa Maria, CA to Seattle</td>
      <td>$5,600–5,900</td>
      <td>+11%</td>
    </tr>
    <tr>
      <td>South Texas to Baltimore</td>
      <td>$5,600–6,400</td>
      <td>−4%</td>
    </tr>
    <tr>
      <td>South Texas to Chicago</td>
      <td>$3,800–4,400</td>
      <td>−3%</td>
    </tr>
  </tbody>
</table>

<p>Trucks were short in Delmarva, South and Central California, and Yakima and Wenatchee.</p>

<p>Each lane page compares the week with that lane’s own history. <a href="/reefer-rates/south-texas-to-chicago/">South Texas to Chicago</a> was $3,800–4,400 a load, mostly $3,800–4,000, which is higher than 154 of its 283 earlier weeks on record. Over the last 52 weeks it was quoted every week, from a low of $2,400 to a high of $7,000, averaging $4,510 at the middle of the range.</p>

<p>The history changes the answer. When I first wrote this, the record only went back to March 2026, and the same rate was higher than just 9 of 37 earlier weeks, which read as cheap. With every week back to April 2021, it’s about average.</p>

<h2 id="what-surprised-me">What surprised me</h2>

<p><strong>USDA’s typos.</strong> This is the most consistent public record of reefer spot rates there is, and it still has mistakes in it. For the week of May 25, 2026, the report has Florida to New York at $53–5,500. The weeks around it were $5,500–5,700 and $5,600–5,800, so that low was almost certainly $5,300 with digits missing. USDA’s own change column for that week says −50%, so the typo made it into their math too. Two months later, South and Central California to Atlanta has a low of $740 for the week of July 20, where the weeks around it start at $7,200.</p>

<p>For a while the board took those numbers at face value, and the Florida to New York page said the lane’s lowest rate on record was $53. Real ranges in the data never span more than about 2.2 times from low to high, so now a low under a quarter of its high is treated as a typo and left off the pages. The CSV still keeps the report exactly as USDA published it, so anyone can check.</p>

<p><strong>Diesel.</strong> The board also tracks EIA’s weekly U.S. average price for diesel, the number most fuel surcharge tables are built on. The week of October 21, 2024, it was $3.553 a gallon. At the end of August 2026 it was $5.599. It then climbed for three straight weeks to $6.529 on September 21, and was $6.179 the week of October 12.</p>

<h2 id="where-it-breaks">Where it breaks</h2>

<ul>
  <li><strong>The rates are USDA’s survey, not a quote.</strong> They’re what reporters heard for spot loads that week. A contract rate, or one specific load, can sit outside the range.</li>
  <li><strong>The board can’t split a rate into fuel and everything else.</strong> USDA’s rates are all-in, so the diesel table sits beside them without saying how much of a move came from fuel.</li>
  <li><strong>The history starts in April 2021.</strong> That’s as far back as USDA’s API goes for this report, and USDA published no report for one week in between. The comparisons on each lane page are against that record, not against every rate ever charged.</li>
  <li><strong>Lanes come and go with the seasons.</strong> A lane missing from this week’s sheet is usually just out of season. Its page stays up with its history.</li>
</ul>

<p>The <a href="/reefer-rates/">Reefer Rate Board</a> has this week’s sheet, a lookup for any lane, the rates into each city and the <a href="/reefer-rates/usda-reefer-truck-rates.csv">whole history as a CSV</a>. The lanes from Mexico also show the live truck waits at their border crossings, from the <a href="/border-waits/">border wait board</a>.</p>
]]></content:encoded>
      <category>logistics</category>
      <category>freight</category>
      <category>cloudflare</category>
      <category>usda</category>
    </item>
    <item>
      <title>I turned USDA&apos;s daily boxed beef report into a price board for every cut</title>
      <link>https://juantorresmi.github.io/blog/usda-boxed-beef-prices-by-cut/</link>
      <guid isPermaLink="true">https://juantorresmi.github.io/blog/usda-boxed-beef-prices-by-cut/</guid>
      <pubDate>Fri, 16 Oct 2026 00:00:00 -0400</pubDate>
      <description>How I built a daily boxed beef price board with a page for every cut from USDA&apos;s LM_XB403 report, what six years of the cutout show, and where it falls short.</description>
      <content:encoded><![CDATA[<p>What is a boneless ribeye worth today, before anyone cuts it into steaks? Packers report every sale of boxed beef to the U.S. Department of Agriculture, and USDA publishes the answer every business day afternoon: the boxed beef cutout and a price for every cut that traded. It comes as a PDF or as raw rows in a datamart, one table per section.</p>

<p>The <a href="/beef-prices/">Boxed Beef Price Board</a> puts it on one page, with a page and the full daily history for every cut. I wanted another project in the same mold as the <a href="/reefer-rates/">reefer rate board</a> and the <a href="/border-waits/">border wait board</a>: a public data source, the site’s Cloudflare Worker, pages search engines can find, and nothing to pay.</p>

<h2 id="the-data">The data</h2>

<p>The report is USDA’s <a href="https://mymarketnews.ams.usda.gov/viewReport/2453">National Daily Boxed Beef Cutout and Boxed Beef Cuts, Negotiated Sales, afternoon edition</a> (LM_XB403). Under Livestock Mandatory Reporting, packers have to report their sales, and USDA publishes the totals:</p>

<ul>
  <li><strong>The cutout</strong>, for Choice and for Select: an estimate of what a carcass is worth broken into the cuts packers sell, in dollars per hundredweight (100 lb) of carcass.</li>
  <li><strong>Seven primal values</strong>: the rib, chuck, round, loin, brisket, short plate and flank.</li>
  <li><strong>One row per cut</strong>, for each grade: the number of trades, the pounds, the low, the high and the weighted average. Each cut is named the way the trade orders it, with its IMPS number and a fat limitation code, like <code class="language-plaintext highlighter-rouge">Rib, ribeye, bnls, heavy (112A 3)</code>.</li>
  <li><strong>Ground beef and trimmings</strong>, sold by lean percentage.</li>
</ul>

<p>USDA’s Livestock Mandatory Reporting datamart serves all of it as JSON with no key. One request with <code class="language-plaintext highlighter-rouge">allSections=true</code> returns the whole day’s report, about 110 KB.</p>

<h2 id="how-it-works">How it works</h2>

<ol>
  <li><strong>The history stays USDA’s.</strong> USDA already keeps every report, and the datamart serves any date range, so nothing on this site stores a second copy of it. A GitHub Action runs twice a day and reads back the CSVs the site already publishes, which hold every day fetched so far. It asks USDA only for what they don’t settle, which on an ordinary run is the last month or two (USDA sometimes corrects a report). The very first run asked for all 82 months since January 2020, about two minutes of downloads.</li>
  <li><strong>A cut that didn’t trade is left blank, never zero.</strong> USDA prints those as $0.00 with no trades. Stored as a price, they’d drag every average toward nothing, so they’re not stored at all.</li>
  <li><strong>The Action rebuilds the site.</strong> It writes the board, a page for each of 58 cuts, primals and ground beef items, the CSVs (the daily cutout and primal values, and one file per year of every cut) and a JSON file of the newest report. The pages are plain HTML.</li>
  <li><strong>A Cloudflare Worker watches for the next report.</strong> Every half hour it asks USDA for the newest report’s summary, a few hundred bytes. When there’s a new one, it fetches the whole report, about 110 KB, and keeps it: one row in a SQLite database on Cloudflare (D1), the only thing it stores. The rebuilds only come twice a day, so a page whose copy is older than a report that should be out asks the Worker and puts the new figures at the top, usually within half an hour of USDA posting them. Otherwise the pages ask nothing.</li>
</ol>

<h2 id="what-it-shows">What it shows</h2>

<p>The report for Friday, October 16, 2026 had the Choice cutout at $372.31 and Select at $353.05, with 103 loads of Choice and 5 of Select sold. A year earlier, October 16, 2025, Choice was $366.11, so the cutout was up 1.7%. The record covers 1,731 business days, from January 2, 2020:</p>

<table>
  <thead>
    <tr>
      <th>Year</th>
      <th>Average Choice cutout</th>
      <th>Low</th>
      <th>High</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>2020</td>
      <td>$237.95</td>
      <td>$200.47</td>
      <td>$475.39</td>
    </tr>
    <tr>
      <td>2021</td>
      <td>$279.49</td>
      <td>$205.27</td>
      <td>$348.03</td>
    </tr>
    <tr>
      <td>2022</td>
      <td>$263.81</td>
      <td>$242.65</td>
      <td>$293.50</td>
    </tr>
    <tr>
      <td>2023</td>
      <td>$299.25</td>
      <td>$264.74</td>
      <td>$343.09</td>
    </tr>
    <tr>
      <td>2024</td>
      <td>$308.15</td>
      <td>$275.90</td>
      <td>$330.47</td>
    </tr>
    <tr>
      <td>2025</td>
      <td>$359.13</td>
      <td>$310.77</td>
      <td>$416.01</td>
    </tr>
    <tr>
      <td>2026, through October 16</td>
      <td>$380.22</td>
      <td>$349.97</td>
      <td>$403.31</td>
    </tr>
  </tbody>
</table>

<p>The record high is from the spring of 2020, when packing plants shut down: $475.39 on May 12. Two months later, on July 17, the cutout hit its low for the whole record, $200.47.</p>

<p>A cutout up 1.7% on the year hides how differently its parts moved. Choice prices per pound, October 16, 2025 against October 16, 2026:</p>

<table>
  <thead>
    <tr>
      <th>Cut</th>
      <th>A year earlier</th>
      <th>Now</th>
      <th>Change</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><a href="/beef-prices/chuck-roll-116a/">Chuck roll, neck off</a> (116A)</td>
      <td>$4.54</td>
      <td>$5.96</td>
      <td>+31.1%</td>
    </tr>
    <tr>
      <td><a href="/beef-prices/ribeye-boneless-heavy-112a/">Ribeye, boneless, heavy</a> (112A)</td>
      <td>$13.85</td>
      <td>$15.47</td>
      <td>+11.7%</td>
    </tr>
    <tr>
      <td><a href="/beef-prices/tenderloin-trimmed-189a/">Tenderloin, trimmed, heavy</a> (189A)</td>
      <td>$18.93</td>
      <td>$16.35</td>
      <td>−13.7%</td>
    </tr>
    <tr>
      <td><a href="/beef-prices/ground-beef-81/">Ground beef, 81% lean</a></td>
      <td>$3.45</td>
      <td>$3.27</td>
      <td>−5.1%</td>
    </tr>
    <tr>
      <td><a href="/beef-prices/fresh-50-lean-trimmings/">Fresh 50% lean trimmings</a></td>
      <td>$1.47</td>
      <td>$0.87</td>
      <td>−41.0%</td>
    </tr>
  </tbody>
</table>

<p>Each cut page ranks the latest price against the cut’s own past year. The heavy boneless ribeye’s $15.47 a pound was higher than on 234 of the other 254 days it traded in the past year. The boneless top sirloin butt was the opposite, higher than on only 2 of 253.</p>

<h2 id="what-the-history-shows">What the history shows</h2>

<p><strong>Every cut has its own season.</strong> Each cut page compares every month with its own year’s average and takes the median over 2020 to 2025. That takes out the drift: the cutout rose about 50% in those years, and without it every later month would look dear. The Choice cutout usually runs highest in June (+5.0%) and lowest in February (−9.5%). The loin cuts swing much harder than that, and they peak at the same time of year:</p>

<table>
  <thead>
    <tr>
      <th>Cut</th>
      <th>Usually highest</th>
      <th>Usually lowest</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><a href="/beef-prices/tri-tip-185c/">Tri-tip</a> (185C)</td>
      <td>June, +28.7%</td>
      <td>October, −12.6%</td>
    </tr>
    <tr>
      <td><a href="/beef-prices/strip-loin-boneless-180/">Strip loin, boneless</a> (180)</td>
      <td>June, +24.0%</td>
      <td>October, −14.6%</td>
    </tr>
    <tr>
      <td><a href="/beef-prices/top-sirloin-butt-184/">Top sirloin butt, boneless</a> (184)</td>
      <td>June, +20.0%</td>
      <td>January, −13.0%</td>
    </tr>
    <tr>
      <td><a href="/beef-prices/ribeye-boneless-heavy-112a/">Ribeye, boneless, heavy</a> (112A)</td>
      <td>November, +14.2%</td>
      <td>February, −11.9%</td>
    </tr>
    <tr>
      <td><a href="/beef-prices/ribeye-lip-on-bone-in-109e/">Ribeye, lip-on, bone-in</a> (109E)</td>
      <td>December, +23.9%</td>
      <td>July, −16.2%</td>
    </tr>
    <tr>
      <td><a href="/beef-prices/ground-beef-93/">Ground beef, 93% lean</a></td>
      <td>June, +2.4%</td>
      <td>January, −3.9%</td>
    </tr>
  </tbody>
</table>

<p>The ribeyes peak at the other end of the year, in November and December. Lean ground beef barely moves at all.</p>

<p><strong>The median matters.</strong> Averaged across the years instead, the numbers say the cutout usually runs 14.6% above its year in May. That’s 2020 alone: one May with closed plants and a record cutout is enough to make every May look expensive. With the median, May is +0.4%.</p>

<p><strong>Select sometimes beats Choice.</strong> Choice has more marbling and nearly always costs more. Not always: Select’s cutout came in above Choice’s on 16 days in the record, once in February 2022 and 15 times between January and May 2026. The widest gap the other way was $39.74, on June 21, 2021.</p>

<h2 id="where-it-breaks">Where it breaks</h2>

<ul>
  <li><strong>It’s negotiated sales only.</strong> Beef sold on formulas or forward contracts is reported separately and isn’t in these numbers.</li>
  <li><strong>Select is thin.</strong> On October 16, Choice sold 103 loads and Select 5. Many cuts have no Select price on a given day, and the ones that do can rest on a handful of trades. The board’s list of the week’s biggest moves only counts cuts with at least 10 trades for that reason.</li>
  <li><strong>They’re wholesale prices, in nominal dollars.</strong> A cut in a box at the packing plant is worth far less a pound than the same cut trimmed and wrapped in a store, and the history isn’t adjusted for inflation.</li>
  <li><strong>Seasons need years.</strong> Six full years is enough to see a pattern, not enough to promise one. The pages say “usually”, and the year-by-month tables show every month, so you can see when a season didn’t hold.</li>
  <li><strong>A day can be missing.</strong> USDA doesn’t publish a price it would have to hide to protect who traded, and federal holidays have no report. Those days are blank.</li>
</ul>

<p>The <a href="/beef-prices/">Boxed Beef Price Board</a> has today’s cutout, the primals, every cut by primal, the biggest moves of the week, the history since 2020 and <a href="/beef-prices/#data">the data as CSV files</a>, free to use with credit to USDA.</p>
]]></content:encoded>
      <category>logistics</category>
      <category>beef</category>
      <category>cloudflare</category>
      <category>usda</category>
    </item>
    <item>
      <title>I built a board of truck wait times at every land border crossing</title>
      <link>https://juantorresmi.github.io/blog/border-truck-wait-times-from-cbp/</link>
      <guid isPermaLink="true">https://juantorresmi.github.io/blog/border-truck-wait-times-from-cbp/</guid>
      <pubDate>Fri, 16 Oct 2026 00:00:00 -0400</pubDate>
      <description>How I built a board of commercial truck wait times at every U.S. land port from CBP&apos;s feed with a Cloudflare Worker, and what the usual waits by hour show.</description>
      <content:encoded><![CDATA[<p>U.S. Customs and Border Protection posts how long commercial trucks are waiting at every land port, about once an hour. Its live page only ever shows the wait right now, though. If you want to know whether Laredo is usually worse on a Thursday afternoon than a Monday morning, that page can’t tell you.</p>

<p>I built the <a href="/border-waits/">Border Wait Board</a> for the same reasons as the <a href="/blog/usda-reefer-truck-rate-board/">reefer rate board</a>: to learn more about Cloudflare and working with APIs, and to improve this site’s SEO, with a page that people searching for a crossing’s wait time can actually find. The two boards also fit together. Produce loads from Mexico wait at these crossings before they roll, so the reefer lanes from South Texas, Nogales and Calexico show the live waits at their crossings.</p>

<h2 id="the-data">The data</h2>

<p>CBP’s <a href="https://bwt.cbp.gov/">Border Wait Times</a> feed is public and needs no key. It lists about 85 ports, and about 50 of them have commercial lanes; the board shows 53, 28 on the Mexican border and 25 on the Canadian. For each one, officers post the wait in the standard commercial lanes and in the FAST lanes (for carriers and drivers in CBP’s Free and Secure Trade program), how many lanes are open, and the time they posted it.</p>

<h2 id="how-it-works">How it works</h2>

<ol>
  <li><strong>A Cloudflare Worker reads the feed every half hour.</strong> It’s the same Worker that runs the reefer board, and both of its hourly cron runs, at 17 and 47 past, read CBP.</li>
  <li><strong>Each reading is kept once, for good.</strong> A reading is stored with the port and the time it was posted, so reading the feed twice in one hour never counts the same reading twice. The Worker holds the last two weeks in a small SQLite database on Cloudflare (D1), and every rebuild adds them to the permanent record: one CSV per year in this site’s repository, where a reading, once kept, is never changed.</li>
  <li><strong>Unknown is not zero.</strong> When lanes are closed or an update is pending, CBP posts no wait. Those are stored as blanks, not as zero minutes, and every average skips them. Counted as zero, a crossing that’s often closed would look fast.</li>
  <li><strong>Times are the port’s own clock.</strong> The feed gives each port’s local date and time, and that’s what “the best hour to cross” has to mean. Its time zone labels aren’t always right, though: Arizona ports say PST, and Port Huron says EST in October. So the board uses the clock time and ignores the labels.</li>
  <li><strong>CBP’s own history fills the gap.</strong> CBP also publishes, for every port, the average wait at each hour of each weekday in a month, from the year before. Until a crossing has a few days of the board’s own readings, its page shows CBP’s numbers for the current month, labeled as CBP’s.</li>
  <li><strong>At least once a day, the site rebuilds.</strong> A GitHub Action copies the Worker’s data into the site: the board, a page for each of 16 major commercial crossings, CSVs of every reading and of the 30-day averages, and a Spanish page for each of the 9 Mexico crossings among them. The Spanish pages use CBP’s own Spanish (“sin demora” for no delay, “carriles generales” for the standard lanes) and point at their English pages, so search engines show the right one. The “right now” columns also refresh from the Worker when a page opens.</li>
</ol>

<h2 id="what-it-shows">What it shows</h2>

<p>The board’s own record only started on October 14, 2026, so for now the usual waits come from CBP’s averages for October last year. In the standard commercial lanes, the six crossings with the longest average wait:</p>

<table>
  <thead>
    <tr>
      <th>Crossing</th>
      <th>Average</th>
      <th>Usually worst</th>
      <th>Usually best</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>El Paso Ysleta–Zaragoza Bridge</td>
      <td>34 min</td>
      <td>Monday 4 PM, 53 min</td>
      <td>Monday 6 AM, 10 min</td>
    </tr>
    <tr>
      <td>Brownsville Veterans International Bridge</td>
      <td>26 min</td>
      <td>Tuesday 2 PM, 67 min</td>
      <td>Monday 8 AM, no wait</td>
    </tr>
    <tr>
      <td>Otay Mesa Commercial Crossing</td>
      <td>25 min</td>
      <td>Monday 10 AM, 41 min</td>
      <td>Friday 9 PM, no wait</td>
    </tr>
    <tr>
      <td>Pharr–Reynosa International Bridge</td>
      <td>23 min</td>
      <td>Monday 4 PM, 49 min</td>
      <td>Tuesday 9 PM, 1 min</td>
    </tr>
    <tr>
      <td>Port Huron Blue Water Bridge</td>
      <td>23 min</td>
      <td>Monday 5 PM, 82 min</td>
      <td>Monday 1 AM, no wait</td>
    </tr>
    <tr>
      <td>Laredo World Trade Bridge</td>
      <td>22 min</td>
      <td>Thursday 5 PM, 51 min</td>
      <td>Monday 7 AM, no wait</td>
    </tr>
  </tbody>
</table>

<p>Four of the six are at their worst on a Monday, and every one is at its best early in the morning or late at night. Averages also hide peaks: Port Huron’s 23-minute average comes with an 82-minute peak at 5 PM on Mondays. Closer to home, the Ambassador Bridge in Detroit averaged just 6 minutes that month, and 3 in the FAST lanes.</p>

<p>When the site last copied the data, on the evening of October 16, 37 of the 53 crossings were posting a wait, and the longest standard-lane wait was 60 minutes at Sweetgrass, Montana.</p>

<h2 id="where-it-breaks">Where it breaks</h2>

<ul>
  <li><strong>Last October isn’t this October.</strong> Until the board has its own month of readings, the usual waits are CBP’s averages from a year ago. Each page says whose numbers it’s showing, and CBP’s published averages for every month and crossing are kept in a <a href="/border-waits/cbp-published-averages.csv">CSV</a> to check the board’s own numbers against.</li>
  <li><strong>Early averages are thin.</strong> A crossing needs about two days of readings before the board will name its own best and worst hour, and even then an hour may rest on two or three readings. The pages show how many readings each one is based on.</li>
  <li><strong>The wait is what CBP posts</strong>, rounded the way CBP rounds it. Texas A&amp;M’s <a href="https://bcis.tti.tamu.edu/">Border Crossing Information System</a> measures crossing times with roadside readers at some ports in Texas, New Mexico and Arizona. That’s a different measure, so the two are worth comparing.</li>
</ul>

<p><em>Updated October 18, 2026: CBP keeps no past readings, so the board’s record started when the Worker did, on October 14. An independent public archive had saved CBP’s whole feed about once an hour since September 13, and its readings are now part of the record, run through the same code the Worker uses and marked as the archive’s. Where both had a reading, they matched 523 times out of 530. The record now goes back to September 13, so the board’s own 30-day averages have taken over from CBP’s. The numbers above are from the October 16 copy. Checking this also turned up a bug: the Worker read CBP’s dates as two-digit months and days, but CBP writes October 1 as 10/1/2026, so from November 1 it would have stopped recording on the first nine days of every month. That’s fixed.</em></p>

<p>The <a href="/border-waits/">Border Wait Board</a> has every commercial crossing right now, the <a href="/border-waits/laredo-world-trade-bridge/">crossing pages</a> have a heat map of the usual wait by day and hour, the Mexico crossings are <a href="/es/tiempos-de-espera/">in Spanish</a> too, and every reading, the 30-day averages and CBP’s published averages are free CSV downloads on the board.</p>
]]></content:encoded>
      <category>logistics</category>
      <category>border</category>
      <category>cloudflare</category>
      <category>cbp</category>
    </item>
    <item>
      <title>I replaced my Minecraft price spreadsheet with a crafting profit calculator</title>
      <link>https://juantorresmi.github.io/blog/minecraft-crafting-profits-without-a-spreadsheet/</link>
      <guid isPermaLink="true">https://juantorresmi.github.io/blog/minecraft-crafting-profits-without-a-spreadsheet/</guid>
      <pubDate>Wed, 07 Oct 2026 00:00:00 -0400</pubDate>
      <description>Why I built Craft Ledger, a Minecraft crafting profit calculator for DonutSMP and other SMPs, how it costs every vanilla recipe, and the crafts it turned up.</description>
      <content:encoded><![CDATA[<p>On DonutSMP, everything has a price on the auction house and the order board, and every craft is a small trade: is this worth making, or should I just sell the materials? I was tracking those prices in an Excel spreadsheet, and I got tired of it. So I built <a href="/craft-ledger.html">Craft Ledger</a>, a Minecraft crafting profit calculator that does the tracking and the math for me. On DonutSMP it pointed me at ender chests, which had a high margin, and I crafted a ton of them.</p>

<p>This is how it works and what it turned up.</p>

<h2 id="what-a-spreadsheet-cant-keep-up-with">What a spreadsheet can’t keep up with</h2>

<p>A spreadsheet is fine for a handful of recipes. You type in the price of paper and leather, write a formula for a book, then one for a bookshelf. The trouble is that Minecraft has a lot of recipes and they feed into each other. Every new item means new formulas, and every price change ripples up the chain.</p>

<p>Craft Ledger flips that around. You never enter a recipe. The full vanilla recipe set is built in, read from the game’s own data files: 1,536 recipes covering 2,770 items, including crafting, smelting, blasting, smoking, campfire cooking, stonecutting and smithing. You only enter prices. The more raw materials you price, the more of the list fills in on its own.</p>

<h2 id="how-it-works">How it works</h2>

<p>For every item it works out two things: what it sells for, and what it costs to make by the cheapest recipe. The interesting part is what counts as “cost.”</p>

<p>Say books sell for 2,000 and a bookshelf for 15,000. A book costs 1,161 to craft from paper and leather, so it’s tempting to say the bookshelf costs six planks plus three books at 1,161. But if you can sell a book for 2,000, putting it into a bookshelf costs you 2,000, because you gave up that sale. That’s opportunity cost, and it’s the default here (I call it <strong>Market</strong> basis):</p>

<table>
  <thead>
    <tr>
      <th>Bookshelf</th>
      <th>Market basis</th>
      <th>Cheapest basis</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>6 oak planks at 20</td>
      <td>120</td>
      <td>120</td>
    </tr>
    <tr>
      <td>3 books</td>
      <td>6,000 (at their sale price)</td>
      <td>3,483 (at what they cost to make)</td>
    </tr>
    <tr>
      <td><strong>Cost</strong></td>
      <td><strong>6,120</strong></td>
      <td><strong>3,603</strong></td>
    </tr>
    <tr>
      <td><strong>Profit at 15,000</strong></td>
      <td><strong>8,880</strong></td>
      <td><strong>11,397</strong></td>
    </tr>
  </tbody>
</table>

<p>Both numbers are right. Market tells you whether the last step is worth doing. Cheapest tells you whether the whole chain beats doing nothing.</p>

<p>Two things made the math harder than a spreadsheet:</p>

<ul>
  <li><strong>Recipes loop.</strong> Nine gold ingots make a block of gold, and a block of gold makes nine ingots. A formula that follows recipes downward never finishes. The ledger instead starts from the prices you know and keeps passing over every recipe, lowering each item’s cost whenever it finds a cheaper way to make it, until nothing changes anymore.</li>
  <li><strong>The top price doesn’t hold.</strong> The best order on the board might only want nine units. The ledger stores the order book and walks it for the number of units you actually plan to sell, so an item that looks great at a volume of one can fall off the list at a volume of a thousand.</li>
</ul>

<p>It also charges fuel for smelting from the burn time of whatever you pick, takes the server’s sale fee off every profit, and marks prices amber after a week and red after a month so stale numbers don’t quietly skew everything above them.</p>

<h2 id="what-it-turned-up">What it turned up</h2>

<p>The ledger opens with 13 example prices. They’re made up, not DonutSMP’s, so they show how it reads rather than what to craft today. With just those, at a volume of 1,000, it ranks:</p>

<table>
  <thead>
    <tr>
      <th>Craft</th>
      <th>Cost</th>
      <th>Sells for</th>
      <th>Profit per unit</th>
      <th>Margin</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Bookshelf</td>
      <td>6,120</td>
      <td>15,000</td>
      <td>8,880</td>
      <td>59.2%</td>
    </tr>
    <tr>
      <td>Book</td>
      <td>1,161</td>
      <td>2,000</td>
      <td>839</td>
      <td>41.9%</td>
    </tr>
    <tr>
      <td>TNT</td>
      <td>1,355</td>
      <td>1,500</td>
      <td>145</td>
      <td>9.7%</td>
    </tr>
    <tr>
      <td>Golden carrot</td>
      <td>5</td>
      <td>15</td>
      <td>10</td>
      <td>66.7%</td>
    </tr>
    <tr>
      <td>Gold ingot (from a block)</td>
      <td>3.89</td>
      <td>4</td>
      <td>0.11</td>
      <td>2.8%</td>
    </tr>
    <tr>
      <td>Block of gold</td>
      <td>36</td>
      <td>35</td>
      <td>−1</td>
      <td>−2.9%</td>
    </tr>
  </tbody>
</table>

<p>Even this small example shows the pattern. Bookshelves earn the most per unit, but the golden carrot has the best margin on the board, and packing gold ingots into blocks loses money even though it feels like it should be free.</p>

<p>That was the real surprise once I put in actual server prices: there are opportunities in a lot of items you would never consider. Ender chests were mine: a high margin, and I crafted a ton of them.</p>

<p>Pricing more items makes the list grow quickly, and the <strong>Coverage</strong> tab ranks the items you haven’t priced by how many crafts each one would unlock, so you know what to look up next.</p>

<h2 id="where-it-breaks">Where it breaks</h2>

<p>It knows vanilla recipes only, so servers with custom items or changed recipes aren’t covered. It also only knows the prices you give it. Prices on a live server move every day, and a stale price deep in a chain throws off every item above it. Buy and sell are one number too, because on DonutSMP you fill on orders. On a server with a wide gap between buying and selling, you’d have to pick which side you mean.</p>

<h2 id="try-it">Try it</h2>

<p><a href="/craft-ledger.html">Craft Ledger</a> is free, needs no account and keeps your prices in your browser. It works on any SMP with an auction house, an order board or player shops. There’s a step-by-step guide for DonutSMP <a href="/craft-ledger.html#guide">below the ledger</a>.</p>
]]></content:encoded>
      <category>minecraft</category>
      <category>donutsmp</category>
      <category>javascript</category>
      <category>opportunity-cost</category>
    </item>
    <item>
      <title>I built an aggregate planning solver that runs in the browser</title>
      <link>https://juantorresmi.github.io/blog/aggregate-planning-solver-in-the-browser/</link>
      <guid isPermaLink="true">https://juantorresmi.github.io/blog/aggregate-planning-solver-in-the-browser/</guid>
      <pubDate>Wed, 07 Oct 2026 00:00:00 -0400</pubDate>
      <description>Why I built a web-based aggregate planning calculator instead of using Excel Solver, how it finds the cheapest level, band and chase plans, and what it shows.</description>
      <content:encoded><![CDATA[<p>My logistics class had an aggregate planning case study. The usual way to solve it is Excel Solver: you lay out the decision variables, write the constraints as formulas and hope nothing is off by one row. That works if you already know Excel well, and it doesn’t if you don’t. I wanted to learn how to build an online solver that handles the data entry and the projections, so that someone without that Excel background could still work the case. That became the <a href="/aggregate-planning/">Aggregate Planner</a>, an aggregate planning calculator that runs entirely in the browser.</p>

<p>This post is how it works, what the sample case shows, and what I learned about solvers along the way.</p>

<h2 id="the-problem">The problem</h2>

<p>Aggregate planning asks a simple question with an annoying number of moving parts. You have a year of monthly demand. You have a workforce that can make so much a month. What’s the cheapest way to cover the gap?</p>

<p>Every option costs something. Hiring and layoffs cost money. Overtime pays a premium. Stock built early has to be stored. Orders filled late cost goodwill. Cases usually ask you to compare three policies:</p>

<ul>
  <li><strong>Level.</strong> Keep the same workforce all year. Build inventory in slow months, use it up in busy ones.</li>
  <li><strong>Chase.</strong> Hire and lay off freely so production follows demand.</li>
  <li><strong>Band.</strong> Let the workforce move, but only between limits you set.</li>
</ul>

<p>The planner solves all three at once, from the same inputs, and puts them side by side.</p>

<h2 id="how-the-solver-works-in-plain-words">How the solver works, in plain words</h2>

<p>Every month gets a handful of decisions: teams hired, teams laid off, teams employed, overtime hours, units made, units held in stock, and orders left late. Those are the variables. Then every month has to obey four rules:</p>

<ol>
  <li>Teams this month = teams last month + hires − layoffs.</li>
  <li>Last month’s stock + this month’s production = this month’s demand + what’s left over (late orders count as negative stock).</li>
  <li>Overtime can’t go past the cap per team.</li>
  <li>Production can’t go past what the teams can make in regular time plus overtime.</li>
</ol>

<p>The goal is to make the total cost of all those decisions as small as possible. That’s a <strong>linear program</strong>: every cost and every rule is a straight-line formula of the variables. For the 12-month sample, it comes to 84 variables and 48 rules.</p>

<p>The catch is people. You can’t hire 6.35 teams. Requiring whole teams turns it into a <strong>mixed-integer program</strong>, which is much harder in general. The solver handles it with <strong>branch and bound</strong>. First it solves the problem with fractions allowed, which gives a cost no whole-team plan can beat. Then it picks a fractional number, say 6.35 teams, and splits the problem in two: one where that month has at most 6 and one where it has at least 7. It solves both halves, throws away any branch that can’t beat the best whole-team plan found so far, and keeps splitting until nothing better can exist.</p>

<p>The solver doing that is <a href="https://highs.dev/">HiGHS</a>, a free, open-source optimizer. It’s compiled to WebAssembly, so it runs inside the page. The page writes the model out as text, hands it to HiGHS in a background thread, and reads the answer back. Nothing you type is sent anywhere.</p>

<h2 id="what-the-sample-case-shows">What the sample case shows</h2>

<p>The page opens with a made-up seasonal product. Demand climbs from 1,600 units in month 1 to 4,100 in month 11. The plant starts with 12 teams of 5 workers. Labor is $25 an hour, overtime $37.50. Hiring costs $500 a worker and a layoff $800. A unit costs $12 a month to hold and $30 a month to leave late. The year has to end with 12 teams, at least 300 units in stock and nothing still late.</p>

<p>With whole teams, HiGHS returns:</p>

<table>
  <thead>
    <tr>
      <th>Policy</th>
      <th>Annual cost</th>
      <th>Saved vs. level</th>
      <th>Peak inventory</th>
      <th>Teams</th>
      <th>Hired · laid off</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Level</td>
      <td>$5,261,040</td>
      <td>—</td>
      <td>3,204</td>
      <td>12 all year</td>
      <td>0 · 0</td>
    </tr>
    <tr>
      <td>Band (10–14)</td>
      <td>$5,062,160</td>
      <td>$198,880</td>
      <td>1,804</td>
      <td>10 to 14</td>
      <td>4 · 4</td>
    </tr>
    <tr>
      <td>Chase</td>
      <td>$4,875,900</td>
      <td>$385,140</td>
      <td>1,088</td>
      <td>7 to 17</td>
      <td>10 · 10</td>
    </tr>
  </tbody>
</table>

<p>Total material is the same $1,992,000 in every plan, because all three make exactly the 33,200 units demand needs. The difference is all in how they pay for labor and storage. The level plan spends $245,040 holding stock that peaks in month 8. The chase plan spends $65,000 on hiring and layoffs instead and keeps holding costs down to $43,920.</p>

<p>A few things in there I didn’t expect:</p>

<ul>
  <li><strong>Chase only saves about 7%.</strong> Total freedom to hire and fire beats a fixed workforce, but not by much. A band of just 10 to 14 teams gets more than half of that saving, with 4 hires instead of 10.</li>
  <li><strong>The cheapest plan runs late on purpose.</strong> Chase leaves 8 units unfilled in month 3 and 8 more in month 8. Each one costs $30 for the month. Adding staff or holding extra stock to cover them would have cost more.</li>
  <li><strong>Whole teams cost very little.</strong> With fractions allowed, the chase plan costs $4,867,016, about $8,900 less, using 6.35 teams in the first month. Branch and bound took 13 nodes to find the best whole-team plan and prove it. Level and band needed only the first node.</li>
</ul>

<h2 id="what-surprised-me">What surprised me</h2>

<p>How accessible and accurate this was. I went in thinking a real solver meant a license, a server or at least a Python setup. Instead, HiGHS gives exact, provably optimal answers in under a second, in a browser tab, for free. You don’t have to take its word for it, either. The page exports a workbook in the usual Solver layout, with live formulas, so you can check every number or run Solver on it again. It’s also not just a classroom trick. The same kind of model plans real production, staffing and inventory.</p>

<h2 id="where-it-breaks">Where it breaks</h2>

<p>The model is only as honest as its assumptions. It treats the demand forecast as known. A real plan has to absorb forecast error, and this one will happily cut inventory to the bone if you tell it demand is certain. Costs are linear too: the 50th hire costs the same as the first, and a team is just as productive in its first month as in its tenth. It plans up to 24 months, and HiGHS stops after 20 seconds if a whole-team model gets hard. The page then suggests allowing fractions or a shorter horizon.</p>

<h2 id="study-mode">Study mode</h2>

<p>The part I’d want a student to use is <strong>Study</strong>. It takes the same plan apart with your own numbers. It shows how the inputs become the model, the solve with fractions allowed, the first branch-and-bound split with both halves solved, HiGHS’s own search log, and shadow prices: what one more unit of demand or capacity in each month would change the cost by. It also shows the exact model text that was sent to the solver.</p>

<p>Two things I hope a student takes away. First, how solvers work: they aren’t magic, just a careful search over a model you wrote down. Second, how much logistics pulls together. This one case needs forecasting, labor planning, cost accounting, inventory policy and optimization all at once, and a good plan comes from understanding all of them.</p>

<p>The <a href="/aggregate-planning/">Aggregate Planner</a> opens on the sample. Change any number and it solves all three plans again, or switch to Study to see how it got there.</p>
]]></content:encoded>
      <category>operations</category>
      <category>optimization</category>
      <category>highs</category>
      <category>aggregate-planning</category>
    </item>
    <item>
      <title>I raced a fruit fly&apos;s brain against Google&apos;s OR-Tools</title>
      <link>https://juantorresmi.github.io/blog/fruit-fly-vs-or-tools/</link>
      <guid isPermaLink="true">https://juantorresmi.github.io/blog/fruit-fly-vs-or-tools/</guid>
      <pubDate>Wed, 30 Sep 2026 00:00:00 -0400</pubDate>
      <description>I put a fruit fly&apos;s navigation circuit, wired from FlyWire, in charge of a delivery truck and raced it against OR-Tools. Where the fly won, and where it didn&apos;t.</description>
      <content:encoded><![CDATA[<p>I’d been seeing people <a href="https://www.ibm.com/think/news/fruit-fly-connectome-brain-map-minecraft-doom">get a simulated fruit fly brain to “play” Doom</a>. Around the same time I was sitting in a 400-level logistics class working through a case on 7-Eleven’s fresh-food deliveries, and the two ideas ran into each other: what if a fly had to solve a logistics problem? Nature has been solving “find food with almost no information” for a very long time. How would a fly’s choices compare to ours, and how would it hold up against the kind of optimizer a real company uses?</p>

<p>So I built it. The <a href="/fruit-fly-dispatcher/">Fruit Fly Dispatcher</a> puts a fruit fly’s navigation circuit in charge of a delivery truck and races it against Google’s <a href="https://developers.google.com/optimization">OR-Tools</a> for a week of deliveries. You can watch any of the runs on that page. This post is how it works and what came out of it.</p>

<h2 id="the-setup">The setup</h2>

<p>One truck, one distribution center, 55 convenience stores, Monday to Sunday. Fresh food sells in breakfast, lunch and dinner rushes off a shelf that only holds 40% of a day’s sales, so the truck has to keep coming back. An empty shelf means a lost sale. The truck drives 30 km/h and every stop takes 5 minutes. Profit is a 30% margin on what actually sells, minus ¥200 for every kilometer driven. The store economics come from the class case.</p>

<p>Four “brains” take turns driving:</p>

<ul>
  <li><strong>OR-Tools, live.</strong> Google’s exact solver (<a href="https://developers.google.com/optimization/cp/cp_solver">CP-SAT</a>), re-planning the rest of the trip at every stop.</li>
  <li><strong>OR-Tools, planned day.</strong> Plans every trip at dawn from that morning’s shelf data.</li>
  <li><strong>Fly rule.</strong> A greedy rule: go wherever earns the most per hour, one stop at a time.</li>
  <li><strong>FlyWire brain.</strong> The fly’s own navigation circuit, built from the connectome.</li>
</ul>

<p>The twist is that each brain is also run at four levels of knowledge:</p>

<ol>
  <li><strong>Company data.</strong> Live shelf levels and GPS, which is what a real dispatcher at 7-Eleven has.</li>
  <li><strong>Perfect memory.</strong> Knows every store and how it usually sells, but only learns shelf levels by smell.</li>
  <li><strong>Learning fly.</strong> Starts knowing only where the depot is and learns the stores as it finds them.</li>
  <li><strong>Dumb fly.</strong> No memory at all. It finds stores only by following their smell, and every day is its first.</li>
</ol>

<p>That split is the whole point. I wanted to separate <em>who makes the decisions</em> from <em>what they know</em>.</p>

<h2 id="putting-a-fly-in-the-drivers-seat">Putting a fly in the driver’s seat</h2>

<p><a href="https://flywire.ai/">FlyWire</a> is a map of every neuron and synapse in an adult fruit fly’s brain, <a href="https://www.nature.com/articles/s41586-024-07558-y">published in Nature in 2024</a>. The part I used is the navigation circuit. One ring of cells keeps track of which way the fly is facing, another set holds a goal direction, and steering cells compare the two to decide which way to turn. When the fly smells something, wind-sensing cells write an “upwind” goal into those same goal cells through their real FlyWire connections. So memory (“the store was over there”) and smell (“something good is upwind”) compete inside the actual wiring.</p>

<p>I tried to keep myself honest here. Every direction the circuit uses was derived from the wiring alone and then checked against something independent. I didn’t feed any published results in. A few things aren’t the connectome: four settings were tuned on simple single-goal steering, two constants come from behavior studies, and the choice of <em>which</em> store to go for is a foraging rule I wrote. The circuit handles the steering, not the whole business.</p>

<p>All the brains share the same reflexes when memory runs out: surge upwind on a whiff, cast crosswind when the scent is lost, then search in a spiral. Across the density sweeps and example weeks, that came to more than 300 simulated weeks.</p>

<h2 id="what-happened">What happened</h2>

<h3 id="knowing-beat-deciding-by-a-lot">Knowing beat deciding, by a lot</h3>

<p>Going from a fly with no memory to live company data is worth about <strong>¥19.7M a week</strong> on average. Swapping one brain for another at the same level of knowledge changes profit by at most <strong>¥3.86M</strong>. What the truck knows mattered far more than how smart the thing driving it was.</p>

<p>Even more surprising: a fly that just <em>remembers</em> where the stores are and how they usually sell earns <strong>99%</strong> of what live point-of-sale data earns. Most of the losses come from not knowing the map at all.</p>

<h3 id="the-fly-won-when-information-was-thin">The fly won when information was thin</h3>

<p>With the same senses and the same truck, the FlyWire brain beat OR-Tools by up to <strong>9%</strong>. That was as a dumb fly, with stores about 128 m apart. In the dense example week the gap was bigger. With no memory, the fly served <strong>55%</strong> of demand against OR-Tools’ <strong>43%</strong>:</p>

<table>
  <thead>
    <tr>
      <th>Stores 65 m apart, profit per week</th>
      <th>OR-Tools</th>
      <th>FlyWire brain</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Company data</td>
      <td>¥32.6M</td>
      <td>¥32.3M</td>
    </tr>
    <tr>
      <td>Perfect memory</td>
      <td>¥32.5M</td>
      <td>¥32.4M</td>
    </tr>
    <tr>
      <td>Learning fly</td>
      <td>¥30.2M</td>
      <td>¥30.8M</td>
    </tr>
    <tr>
      <td>Dumb fly</td>
      <td>¥13.4M</td>
      <td><strong>¥17.3M</strong></td>
    </tr>
  </tbody>
</table>

<p>An optimizer is only as good as what it’s told. Give it good information and it’s the best there is. Take the information away and it’s planning around stores it doesn’t know exist. The fly doesn’t plan much at all. It reacts to what it smells, and blending that with whatever it remembers lets it pick up stores it passes on the way. When stores are packed close together, that turns out to be a very good strategy.</p>

<h3 id="spread-the-stores-out-and-the-solver-takes-over">Spread the stores out and the solver takes over</h3>

<p>This is the part I didn’t expect going in. It only works when stores are close. Spread them out and every wrong turn gets expensive:</p>

<table>
  <thead>
    <tr>
      <th>Stores 2.5 km apart, profit per week</th>
      <th>OR-Tools</th>
      <th>FlyWire brain</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Company data</td>
      <td>¥23.5M</td>
      <td>¥21.8M</td>
    </tr>
    <tr>
      <td>Perfect memory</td>
      <td>¥23.3M</td>
      <td>¥21.0M</td>
    </tr>
    <tr>
      <td>Learning fly</td>
      <td>¥13.5M</td>
      <td>¥10.2M</td>
    </tr>
    <tr>
      <td>Dumb fly</td>
      <td>¥4.2M</td>
      <td>¥2.8M</td>
    </tr>
  </tbody>
</table>

<p>OR-Tools wins at every level. By the time stores are 5.1 km apart, the learning fly earns 47% less than the solver. Exact routing also only starts paying off (up to 10% over the greedy rule) once stores are about 1.3 km or more apart. Below roughly 640 m, the simple rule ties it.</p>

<h3 id="density-explained-the-7-eleven-case">Density explained the 7-Eleven case</h3>

<p>The case talks about 7-Eleven Japan delivering fresh food about three times a day, against about once a day in the US. I never told the model to do that. It came out on its own. With stores packed close, one truck manages about <strong>3.7</strong> deliveries per store per day. Spread the same 55 stores out and it drops to <strong>1.1</strong>. Past about 546 m between stores, the truck can’t come back often enough, no matter which brain is driving. Delivery frequency isn’t really a policy you pick. It follows from how close your stores are.</p>

<h3 id="learning-and-what-bad-gps-does-to-it">Learning, and what bad GPS does to it</h3>

<p>A learning fly in a dense area goes from serving 70% of demand on Monday to 99% by Sunday, and knows 50+ stores by Tuesday. In the spread-out layout it only gets from 40% to 45% and finds 28 of the 55 stores all week. Getting a new driver or a new area up to speed has a real cost, and that cost grows with distance.</p>

<p>I also added drift to the truck’s sense of its own position. A learning fly records its map while it’s drifting, so the map itself ends up wrong. FlyWire lost 54% of its profit, OR-Tools 68% and the greedy rule 70%. The fly held up best because smell corrects a bad memory on the way in.</p>

<h2 id="what-i-took-from-it">What I took from it</h2>

<ul>
  <li><strong>Fix the information before the algorithm.</strong> Clean store locations and demand history got 99% of the value of live data.</li>
  <li><strong>Optimizers earn their keep when stops are far apart.</strong> In a tight cluster, a simple rule ties an exact solver.</li>
  <li><strong>When information is poor, let the edge react.</strong> A driver who stops where the shelf looks empty can beat a plan built on bad data.</li>
  <li><strong>Density drives frequency.</strong> Cluster the stores and frequent fresh delivery follows.</li>
</ul>

<p>The fly didn’t beat Google in general, and I didn’t expect it to. What it did was show where each approach makes sense. In a tight network where nobody knows much, a brain that evolved to find food by smell is really good at logistics. Where the map is big and every kilometer costs money, you want the solver.</p>

<p>Every run is on the <a href="/fruit-fly-dispatcher/">Fruit Fly Dispatcher</a> page. You can pick any brain at any level and watch its whole week, or drag the memory and smell inputs around inside the circuit yourself.</p>
]]></content:encoded>
      <category>logistics</category>
      <category>simulation</category>
      <category>or-tools</category>
      <category>flywire</category>
    </item>
  </channel>
</rss>
