<?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>Forem: manja316</title>
    <description>The latest articles on Forem by manja316 (@manja316).</description>
    <link>https://forem.com/manja316</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%2F3807032%2F019bc956-974c-46d5-880d-00fcfa5fabf7.jpeg</url>
      <title>Forem: manja316</title>
      <link>https://forem.com/manja316</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/manja316"/>
    <language>en</language>
    <item>
      <title>Building a free Polymarket screener: how I turned 13,963 markets into a single scannable page</title>
      <dc:creator>manja316</dc:creator>
      <pubDate>Tue, 26 May 2026 10:51:37 +0000</pubDate>
      <link>https://forem.com/manja316/building-a-free-polymarket-screener-how-i-turned-13963-markets-into-a-single-scannable-page-nna</link>
      <guid>https://forem.com/manja316/building-a-free-polymarket-screener-how-i-turned-13963-markets-into-a-single-scannable-page-nna</guid>
      <description>&lt;p&gt;Polymarket has thousands of markets. Their UI is built for depth on a single market — bet slip, orderbook, charts — not for &lt;em&gt;scanning&lt;/em&gt; across the universe. If you want to ask "what dropped 20pp overnight" or "which crypto markets are sub-20¢ with real volume," there's no built-in view.&lt;/p&gt;

&lt;p&gt;So I built one. It's free, it's static, it rebuilds from the public Gamma API every few hours, and the source is on GitHub. This post is how it works.&lt;/p&gt;

&lt;h2&gt;
  
  
  The data layer
&lt;/h2&gt;

&lt;p&gt;The Polymarket Gamma API (&lt;code&gt;https://gamma-api.polymarket.com/markets&lt;/code&gt;) is paginated and unauthenticated. The "active universe" is much smaller than the lifetime market count Polymarket advertises:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch_active_markets&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://gamma-api.polymarket.com/markets&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;closed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;false&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;limit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;offset&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;
        &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That pulls ~1,200 currently-tradable markets in 3 paginated calls. The other ~12,800 indexed are resolved or expired — useful for backtesting but not for a "what's moving now" screener.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ranking: movers vs volume
&lt;/h2&gt;

&lt;p&gt;Two top-of-page lists:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Top 24h movers:&lt;/strong&gt; sorted by &lt;code&gt;abs(one_day_change)&lt;/code&gt; descending, filtered to &lt;code&gt;volume24hr &amp;gt; 1000&lt;/code&gt; to drop dust.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Volume leaders:&lt;/strong&gt; sorted by &lt;code&gt;volume24hr&lt;/code&gt; descending. Mostly the same 8-10 megamarkets day-to-day (election props, BTC year-end), which is &lt;em&gt;exactly&lt;/em&gt; why you need the movers view.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Crash signal:&lt;/strong&gt; &lt;code&gt;one_day_change &amp;lt;= -0.15&lt;/code&gt; — a proxy for "fell off a recent local high." Backtested on a separate dataset (5,629 events, see &lt;a href="https://github.com/LuciferForge/cross-signal-data" rel="noopener noreferrer"&gt;cross-signal-data&lt;/a&gt;) at 73% mean-reversion rate, but the live screener column is a &lt;em&gt;proxy&lt;/em&gt; not the same signal — caveats are in the repo's &lt;a href="https://github.com/LuciferForge/polyscope/discussions/2" rel="noopener noreferrer"&gt;methodology discussion&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why static + GitHub Pages
&lt;/h2&gt;

&lt;p&gt;The screener regenerates as a flat HTML file every few hours via a single Python script. No backend, no database, no auth, no costs. The whole site is &lt;code&gt;docs/index.html&lt;/code&gt; + per-market detail pages + a small CSS file. Deploy = &lt;code&gt;git push&lt;/code&gt;. Total hosting bill = $0.&lt;/p&gt;

&lt;p&gt;The tradeoff: data is up to a few hours stale. For a screener that's a feature, not a bug — you're scanning for setups, not executing in microseconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd build next (and won't, alone)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;WebSocket layer for live price ticks on top of the static base&lt;/li&gt;
&lt;li&gt;Open-interest column (not in Gamma; needs CLOB orderbook crawl)&lt;/li&gt;
&lt;li&gt;Alerts: "ping me when any crypto market crosses 20pp overnight"&lt;/li&gt;
&lt;li&gt;A "movers within volume" intersection view (asked about it in &lt;a href="https://github.com/LuciferForge/polyscope/discussions/" rel="noopener noreferrer"&gt;Discussion #5&lt;/a&gt; — feedback welcome)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Live: &lt;a href="https://luciferforge.github.io/polyscope/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=polyscope-week22" rel="noopener noreferrer"&gt;https://luciferforge.github.io/polyscope/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=polyscope-week22&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Source: &lt;a href="https://github.com/LuciferForge/polyscope" rel="noopener noreferrer"&gt;https://github.com/LuciferForge/polyscope&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Underlying historical dataset (separate $9 product if you want backtest data): &lt;a href="https://manja8.gumroad.com/l/agyjd?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=polyscope-week22" rel="noopener noreferrer"&gt;https://manja8.gumroad.com/l/agyjd?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=polyscope-week22&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The screener itself stays free forever. The historical SQLite dataset (10.8M snapshots, 43+ days of depth) is what funds the hosting.&lt;/p&gt;

</description>
      <category>polymarket</category>
      <category>opensource</category>
      <category>datascience</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Every Indian GST invoice generator forces signup. I built one that doesn't.</title>
      <dc:creator>manja316</dc:creator>
      <pubDate>Thu, 21 May 2026 22:51:54 +0000</pubDate>
      <link>https://forem.com/manja316/every-indian-gst-invoice-generator-forces-signup-i-built-one-that-doesnt-ag2</link>
      <guid>https://forem.com/manja316/every-indian-gst-invoice-generator-forces-signup-i-built-one-that-doesnt-ag2</guid>
      <description>&lt;p&gt;I needed to send one GST invoice last week. Took me 25 minutes.&lt;/p&gt;

&lt;p&gt;Vyapar wanted me to sign up. Zoho Books wanted me to sign up. Tally wanted me to install desktop software and walked me through a 3-screen wizard. The Indian government's e-invoice portal needed an aadhaar OTP and a separate "tax payer login."&lt;/p&gt;

&lt;p&gt;I just needed: enter buyer GSTIN + line items → PDF.&lt;/p&gt;

&lt;p&gt;So I built it. Single page, runs in your browser, generates a compliant tax invoice in 60 seconds. Free, open source, no signup, no analytics on your form data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Live demo:&lt;/strong&gt; &lt;a href="https://gst.protodex.io?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=gst-invoice-2026-05-22" rel="noopener noreferrer"&gt;https://gst.protodex.io&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's what's actually in it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a GST invoice needs (the bureaucratic part)
&lt;/h2&gt;

&lt;p&gt;Rule 46 of the CGST Rules lists 16 mandatory fields. The interesting ones from a code perspective:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;GSTIN of supplier and recipient&lt;/strong&gt; (15 chars, format: &lt;code&gt;27ZYXWV9876E1A2&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HSN/SAC code per line item&lt;/strong&gt; — 4 digits if your turnover ≤ ₹5cr, 6 digits if more&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GST split: CGST + SGST if same state, IGST if different state&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Place of supply&lt;/strong&gt; — determines the split logic above&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The split is where most calculators get it wrong. Naive implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tax&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;gstPercent&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;100&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;cgst&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tax&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// ← WRONG when seller != buyer state&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sgst&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tax&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Correct implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sameState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sellerStateCode&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;buyerStateCode&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;tax&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;gstPercent&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;100&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;cgst&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sameState&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;tax&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&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;sgst&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sameState&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;tax&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&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;igst&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sameState&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;tax&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your seller is in Karnataka (KA) and your buyer is in Maharashtra (MH), it's IGST — a single tax that the Central government collects and redistributes. Same state: CGST + SGST, split equally between Central and State.&lt;/p&gt;

&lt;p&gt;I bundled the top 40 HSN/SAC codes most SMBs and freelancers use (software dev &lt;code&gt;998314&lt;/code&gt;, hosting &lt;code&gt;998433&lt;/code&gt;, legal &lt;code&gt;998212&lt;/code&gt;, retail goods classes &lt;code&gt;8471&lt;/code&gt; for computers, &lt;code&gt;6109&lt;/code&gt; for T-shirts, etc.). Full official list is ~150k codes at &lt;a href="https://cbic-gst.gov.in/" rel="noopener noreferrer"&gt;cbic-gst.gov.in&lt;/a&gt; if you need an obscure category.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the PDF gets generated (no server)
&lt;/h2&gt;

&lt;p&gt;The PDF is built entirely client-side using &lt;a href="https://github.com/parallax/jsPDF" rel="noopener noreferrer"&gt;jsPDF&lt;/a&gt; + the &lt;code&gt;jspdf-autotable&lt;/code&gt; plugin. The whole pipeline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;jsPDF&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;jspdf&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;jspdf-autotable&lt;/span&gt;&lt;span class="dl"&gt;'&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;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;jsPDF&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;unit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;a4&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;TAX INVOICE&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;105&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;center&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;autoTable&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;startY&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;head&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Description&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;HSN&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Qty&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Rate&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Total&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;lineItems&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;toRow&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;invoice.pdf&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No server, no upload, no API. Your invoice data never leaves the tab. Open DevTools → Network → you'll see zero requests after page load.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I deliberately didn't build
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GSTR-1 filing&lt;/strong&gt; — that's a monthly compliance feature. Use Zoho/Vyapar.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;E-invoice / IRN generation&lt;/strong&gt; — mandatory only if turnover &amp;gt; ₹5cr. The handful of buyers above that threshold already use enterprise tools.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recurring invoices&lt;/strong&gt; — if you need this, you need accounting software anyway.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Server-side persistence&lt;/strong&gt; — invoices auto-save to your browser's &lt;code&gt;localStorage&lt;/code&gt;. Refresh-safe, machine-bound. Switch machines = re-enter once.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What I learned about Indian tax software
&lt;/h2&gt;

&lt;p&gt;It's a market where the dominant tools (Tally, Zoho, Vyapar) compete on feature breadth and SaaS pricing, but they all skip the use case I had: &lt;strong&gt;"I send one invoice every other month and just want a PDF."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That's a long tail problem — too small for SaaS, too irregular for a Tally install, too compliance-heavy for a Word template. So everyone falls back to copy-pasting an Excel template they downloaded from a forum 3 years ago. Or hires a CA for what's a 60-second task.&lt;/p&gt;

&lt;p&gt;The free no-signup tier is the right shape for that user.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;Things on the roadmap if there's interest:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Logo upload&lt;/strong&gt; (currently the PDF is text-only)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Save invoice history&lt;/strong&gt; locally (right now &lt;code&gt;localStorage&lt;/code&gt; keeps the form, not the past PDFs)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CSV export of line items&lt;/strong&gt; for accounting handoff&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;e-Way bill generator&lt;/strong&gt; for goods movement &amp;gt; ₹50k (separate compliance category)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let me know which of these you'd actually use. If it's just "I need logo upload," I'll ship that this week.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Source / try it:&lt;/strong&gt; &lt;a href="https://gst.protodex.io?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=gst-invoice-2026-05-22" rel="noopener noreferrer"&gt;https://gst.protodex.io&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is part of &lt;a href="https://protodex.io?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=gst-invoice-2026-05-22" rel="noopener noreferrer"&gt;protodex.io&lt;/a&gt; — a small set of free utilities focused on Indian compliance + global dev work. Sister sites: &lt;a href="https://property.protodex.io" rel="noopener noreferrer"&gt;property.protodex.io&lt;/a&gt; (Indian property total-cost calculator), &lt;a href="https://cron.protodex.io" rel="noopener noreferrer"&gt;cron.protodex.io&lt;/a&gt; (cron expression explainer with systemd timer output).&lt;/p&gt;

</description>
      <category>india</category>
      <category>opensource</category>
      <category>webdev</category>
      <category>productivity</category>
    </item>
    <item>
      <title>I rebuilt crontab.guru with three features I needed every week</title>
      <dc:creator>manja316</dc:creator>
      <pubDate>Wed, 20 May 2026 22:51:31 +0000</pubDate>
      <link>https://forem.com/manja316/i-rebuilt-crontabguru-with-three-features-i-needed-every-week-1ieh</link>
      <guid>https://forem.com/manja316/i-rebuilt-crontabguru-with-three-features-i-needed-every-week-1ieh</guid>
      <description>&lt;p&gt;&lt;a href="https://crontab.guru" rel="noopener noreferrer"&gt;crontab.guru&lt;/a&gt; is one of the best small dev tools on the internet. It explains a cron expression in plain English instantly. I've used it for ~8 years.&lt;/p&gt;

&lt;p&gt;But every time I came back, I wanted three things it doesn't do.&lt;/p&gt;

&lt;p&gt;So I built them. Live at &lt;a href="https://cron.protodex.io?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=cron-rebuilt-2026w21" rel="noopener noreferrer"&gt;cron.protodex.io&lt;/a&gt; — same dark-mode style, single page, no signup, runs offline. Here's what's different.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Plain English → cron (the reverse direction)
&lt;/h2&gt;

&lt;p&gt;crontab.guru is cron → English. Once you've written cron three times, you don't need the explanation. You need help going the other way: "I want this to run every weekday morning at 9 — what's the cron?"&lt;/p&gt;

&lt;p&gt;I added a generator. Type "every 15 minutes during business hours" → get back &lt;code&gt;*/15 9-17 * * 1-5&lt;/code&gt;. It's not full natural language (I haven't gone to NLP), but the common patterns work: "every X minutes", "every X hours", "daily at HH:MM", "weekly on DAY at HH:MM", "monthly on day N".&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Next 10 actual run times
&lt;/h2&gt;

&lt;p&gt;When you're debugging "did my cron actually fire?" you want to see: "next run is in 47 minutes, then 4 hours from now, then..." Most cron tools show the expression but not the upcoming firings.&lt;/p&gt;

&lt;p&gt;The naive approach — increment a Date by one minute, test each — works but dies on sparse schedules:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&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="nf"&gt;matchesAllFields&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="nx"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setMinutes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getMinutes&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&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;That works for &lt;code&gt;*/5 * * * *&lt;/code&gt; (every 5 min) but dies on &lt;code&gt;@yearly&lt;/code&gt; — you'd walk 525,600 minutes (one year) just to find the second run. Slow on V8, times out in browser.&lt;/p&gt;

&lt;p&gt;Smart-skip fixes it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&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="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;matchesField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;month&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getMonth&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setMonth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getMonth&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// jump to next month&lt;/span&gt;
    &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setHours&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&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="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;matchesField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dom&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getDate&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setDate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getDate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// jump to next day&lt;/span&gt;
    &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setHours&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;// ... same for hour, minute&lt;/span&gt;
  &lt;span class="nx"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setMinutes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getMinutes&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&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;&lt;code&gt;@yearly&lt;/code&gt; now resolves in 3ms instead of timing out. Also correctly handles leap-year crons like &lt;code&gt;0 0 29 2 *&lt;/code&gt; — returns Feb 29 of 2028, 2032, 2036, 2040, 2044.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Systemd timer equivalent
&lt;/h2&gt;

&lt;p&gt;Most modern Linux is systemd-first. Whenever I converted a cron job into a managed service, I had to mentally translate &lt;code&gt;0 9 * * 1-5&lt;/code&gt; into:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[Timer]&lt;/span&gt;
&lt;span class="py"&gt;OnCalendar&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Mon..Fri *-*-* 09:00:00&lt;/span&gt;
&lt;span class="py"&gt;Persistent&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;RandomizedDelaySec&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;30&lt;/span&gt;

&lt;span class="nn"&gt;[Install]&lt;/span&gt;
&lt;span class="py"&gt;WantedBy&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;timers.target&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The tool generates the full &lt;code&gt;[Timer]&lt;/code&gt; block side-by-side with the cron explanation. With the sensible defaults baked in: &lt;code&gt;Persistent=true&lt;/code&gt; (catch up on missed runs after reboot), &lt;code&gt;RandomizedDelaySec=30&lt;/code&gt; (avoid thundering herds on tight-fleet deploys).&lt;/p&gt;

&lt;h2&gt;
  
  
  What this is built on
&lt;/h2&gt;

&lt;p&gt;Pure vanilla JS. No framework. No bundler. ~6KB minified. Source structure is two files: &lt;code&gt;index.html&lt;/code&gt; (which has all the JS inline) and the shared protodex stylesheet.&lt;/p&gt;

&lt;p&gt;Hosted on Cloudflare Pages free tier. Works offline once the page loads — there's no API call at runtime, the entire parser + describer + next-run calculator + systemd generator runs in your browser.&lt;/p&gt;

&lt;h2&gt;
  
  
  Edge cases I haven't handled
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;6-field Quartz format&lt;/strong&gt; (with leading seconds) — only standard 5-field cron&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Year field&lt;/strong&gt; — some forks (e.g. Synology) accept a 6th year field. Not supported.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Named days with mixed numeric&lt;/strong&gt; — &lt;code&gt;MON-FRI,0,6&lt;/code&gt; works in some implementations. Not here.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Patches welcome if anyone hits these.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Try it:&lt;/strong&gt; &lt;a href="https://cron.protodex.io?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=cron-rebuilt-2026w21" rel="noopener noreferrer"&gt;cron.protodex.io&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is part of &lt;a href="https://protodex.io?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=cron-rebuilt-2026w21" rel="noopener noreferrer"&gt;protodex.io&lt;/a&gt; — a small set of free tools focused on dev utilities and Indian compliance.&lt;/p&gt;

</description>
      <category>linux</category>
      <category>devops</category>
      <category>sysadmin</category>
      <category>productivity</category>
    </item>
    <item>
      <title>I built an airdrop scanner that tells crypto farmers how much they've already lost. Most see a number they don't like.</title>
      <dc:creator>manja316</dc:creator>
      <pubDate>Tue, 19 May 2026 12:30:57 +0000</pubDate>
      <link>https://forem.com/manja316/i-built-an-airdrop-scanner-that-tells-crypto-farmers-how-much-theyve-already-lost-most-see-a-57e5</link>
      <guid>https://forem.com/manja316/i-built-an-airdrop-scanner-that-tells-crypto-farmers-how-much-theyve-already-lost-most-see-a-57e5</guid>
      <description>&lt;p&gt;Most airdrop tools are dishonest by design. They sell hype — "you're 92% eligible!" — and route you to bridges they get affiliate commissions on, with no signal at all about whether the bridging is actually worth your gas.&lt;/p&gt;

&lt;p&gt;I spent the last few weeks building a different kind of scanner. The headline number on the overview tab isn't your eligibility score. It's the dollar amount you've &lt;strong&gt;already&lt;/strong&gt; spent on gas across all your wallets and chains.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://protodex.io/airdrop.html" rel="noopener noreferrer"&gt;&lt;strong&gt;Try it (free, no signup, your keys never leave the browser): protodex.io/airdrop.html&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The reality check most tools don't show you
&lt;/h2&gt;

&lt;p&gt;I asked 12 friends running multi-wallet airdrop farms to scan their setups. &lt;strong&gt;Every single one&lt;/strong&gt; was net negative on lifetime gas spent versus airdrop USD received. Including the people who got into Hyperliquid early.&lt;/p&gt;

&lt;p&gt;The era when bridging once to Arbitrum got you UNI-tier money is over. Sybil filters have gotten aggressive (Berachain wiped 30–60% of farmed wallets, ZK was similar), per-wallet allocations are smaller, and mainnet ETH gas is brutal. So I built the tool I wanted, not the tool that sells better.&lt;/p&gt;

&lt;p&gt;Three things this scanner does that I don't think others do:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Pairwise Sybil clustering detection
&lt;/h3&gt;

&lt;p&gt;Protocols increasingly cluster wallets by day-overlap. If your 5 wallets all show activity on the same days — even if you tried to vary the transaction types — they look like one farmer to the allocator. The Berachain TGE filter explicitly used this signal.&lt;/p&gt;

&lt;p&gt;I implemented a pairwise comparison: for each pair of wallets, count shared active days as a fraction of the smaller wallet's total activity. If overlap &amp;gt; 65%, surface a warning with the actual day count and a recommendation to stagger.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;detectSybilClusters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;allAddrs&lt;/span&gt;&lt;span class="p"&gt;)&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;clusters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;allAddrs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;allAddrs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&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;daysA&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;Set&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;daysB&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;Set&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;allAddrs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;daysList&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[]).&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;daysA&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
      &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;allAddrs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;daysList&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[]).&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;daysB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&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;daysA&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;daysB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;continue&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;overlap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;daysA&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;daysB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;length&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;pct&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;overlap&lt;/span&gt; &lt;span class="o"&gt;/&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;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;daysA&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;daysB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;size&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;pct&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;0.65&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;clusters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&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;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;clusters&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;Not a perfect proxy for protocol-side clustering (they also use IP, transfer graph, behavioral fingerprints), but day-overlap is the easiest leak to detect locally and the easiest to fix.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Pattern-aware scoring, not volume-only
&lt;/h3&gt;

&lt;p&gt;Volume-only scoring is exactly what Sybil farmers optimize for, and exactly what protocols penalize. So I added two multipliers on top of the base score:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Diversity multiplier&lt;/strong&gt;: unique counterparties / 50, capped at 1.2x&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistency multiplier&lt;/strong&gt;: if activity spread &amp;gt; 30 days, +10%
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;scoreProto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;proto&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&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;breakdown&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="nx"&gt;proto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;criteria&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&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;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;proto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;]?.[&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;0&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;pts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pts&lt;/span&gt;
              &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;?&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;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pts&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.75&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
              &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;?&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;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pts&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
              &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;?&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;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pts&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.25&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
              &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;total&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;pts&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;breakdown&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;val&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;max&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pts&lt;/span&gt; &lt;span class="p"&gt;});&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;cps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;proto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;]?.&lt;/span&gt;&lt;span class="nx"&gt;counterpartiesCount&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;0&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;spread&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;proto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;]?.&lt;/span&gt;&lt;span class="nx"&gt;spread&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;0&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;diversityMult&lt;/span&gt; &lt;span class="o"&gt;=&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;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;cps&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;50&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;consistencyMult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;spread&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mf"&gt;1.1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;score&lt;/span&gt;&lt;span class="p"&gt;:&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;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&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;total&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;diversityMult&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;consistencyMult&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;So 500 transactions to one contract score worse than 200 transactions across 20 contracts. Which matches how actual allocations work.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Gas cost reality, in USD, headlined
&lt;/h3&gt;

&lt;p&gt;Every outgoing transaction has &lt;code&gt;gasUsed * gasPrice&lt;/code&gt;. Sum that across chains, multiply by live native-token prices from CoinGecko, surface in USD. That's the headline number.&lt;/p&gt;

&lt;p&gt;Most users see a number they're not happy about. That's the point.&lt;/p&gt;

&lt;h2&gt;
  
  
  The tech stack
&lt;/h2&gt;

&lt;p&gt;Whole thing is a single HTML file:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;React 18 + Babel standalone via unpkg CDN — no build step, no bundler, no Vercel deployment dance&lt;/li&gt;
&lt;li&gt;1,500 lines, ~83KB on the wire&lt;/li&gt;
&lt;li&gt;Deploys to GitHub Pages on &lt;code&gt;git push&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;localStorage for API keys + wallets (nothing touches my server)&lt;/li&gt;
&lt;li&gt;CSP locked to explorer endpoints + CoinGecko + the email-capture form&lt;/li&gt;
&lt;li&gt;Parallel batched scan (5 in-flight with Promise.allSettled and 429/503 backoff) → 3-wallet × 13-chain scan in ~30 seconds&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Etherscan V2 unified API now covers most major EVM chains with one key. Sonicscan, Routescan (for Berachain), and Monad's testnet explorer get special-cased fallbacks.&lt;/p&gt;

&lt;p&gt;I deliberately picked the single-HTML-file path over a full Next.js app because:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The trust signal of "this fits in one tab and I can audit it in 10 minutes" matters for a tool that handles your wallet history.&lt;/li&gt;
&lt;li&gt;The protocol list will go stale fast — I'd rather edit a single file than rebuild a deploy.&lt;/li&gt;
&lt;li&gt;No backend means I literally cannot collect your keys even if I wanted to.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What's curated for May 2026
&lt;/h2&gt;

&lt;p&gt;Most of the "famous" airdrops have already happened. Listing them as "active opportunities" would be dishonest. So:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;HYPE (Hyperliquid), BERA (Berachain), ZK (zkSync), SCR (Scroll), MODE, S (Sonic), INIT (Initia), ES (Eclipse), MON (Monad)&lt;/strong&gt; — all dropped. Demoted to S2/S3 farming with realistic likelihood %.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Symbiotic, Mira Network, Etherealize, Monad testnet&lt;/strong&gt; — actually pre-token, surfaced as S-tier.&lt;/li&gt;
&lt;li&gt;HyperEVM, Berachain S2, Sonic S2, Linea LXP-L still farmable.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The list goes stale within weeks of any new TGE. Building a daily-cron registry update is the next thing on the list.&lt;/p&gt;

&lt;h2&gt;
  
  
  Honest about monetization
&lt;/h2&gt;

&lt;p&gt;The bridge action items use affiliate-tracked URLs (deBridge, Orbiter, CoinDCX for the India onramp). They're labelled in the code; the source is public. The scanner stays free forever. There's no premium tier, no subscription, no "pro features coming soon."&lt;/p&gt;

&lt;p&gt;The closest thing to a paywall is the email signup at the bottom for a weekly "airdrop reality check" newsletter. Optional.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd love feedback on
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Sybil overlap threshold (65%)&lt;/strong&gt; — too aggressive? too lax? I picked it from intuition + a couple of public TGE post-mortems. Empirical calibration would be better.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The scoring weights per protocol&lt;/strong&gt; — heuristic. If you've actually been allocated by one of these protocols, your input on what mattered would improve the calibration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Missing protocols&lt;/strong&gt; — there are several pre-token plays I almost certainly missed. Drop names in comments.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Failure modes&lt;/strong&gt; — if the scanner shows wrong data for your wallet, I want to know. Privacy is preserved (your data never reaches me) but I'd love a screenshot or paste of the per-criterion breakdown.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Source + live tool: &lt;a href="https://protodex.io/airdrop.html" rel="noopener noreferrer"&gt;&lt;strong&gt;protodex.io/airdrop.html&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you scan and find your gas number is depressing — you're not alone. Most farmers are net negative. That's the honest reality. Decide eyes-open from here.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>react</category>
      <category>crypto</category>
    </item>
    <item>
      <title>Claude Code Skills Pack — 15 Production Skills, 5 Hooks, 7 Slash Commands ($99 lifetime)</title>
      <dc:creator>manja316</dc:creator>
      <pubDate>Tue, 19 May 2026 09:53:34 +0000</pubDate>
      <link>https://forem.com/manja316/claude-code-skills-pack-15-production-skills-5-hooks-7-slash-commands-99-lifetime-5fd0</link>
      <guid>https://forem.com/manja316/claude-code-skills-pack-15-production-skills-5-hooks-7-slash-commands-99-lifetime-5fd0</guid>
      <description>&lt;p&gt;I've been running Claude Code as my daily IDE for 6+ months. The Anthropic docs are great, but going from "I installed it" to "I have a workflow that actually compounds across sessions" took ~200 hours of trial and error.&lt;/p&gt;

&lt;p&gt;I packaged everything I learned. Today it's available as a single zip.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://skills.protodex.io" rel="noopener noreferrer"&gt;skills.protodex.io&lt;/a&gt;&lt;/strong&gt; — $99 lifetime, single-developer license, 7-day refund&lt;/p&gt;

&lt;h2&gt;
  
  
  What's in the pack
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;15 production skills&lt;/strong&gt; — not toy examples, things I use weekly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;session-start-gate&lt;/code&gt; — 60-second orientation at session start (reads journal, git state, open todos, daemons)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;commit-quality&lt;/code&gt; — writes real commit messages from your staged diff&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pr-author&lt;/code&gt; — generates PR descriptions: summary, why, test plan, risks, rollback&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;diff-reviewer&lt;/code&gt; — pre-commit review for security, bugs, missing tests&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;test-gap-finder&lt;/code&gt; — finds missing test cases by branch analysis&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dependency-safety&lt;/code&gt; — before any dep bump: changelog, CVEs, call-site impact&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;secret-scanner&lt;/code&gt; — high-confidence + heuristic patterns, triages false positives&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;onboarding-orient&lt;/code&gt; — land in a new repo, get a 1-page brief in 60s&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;flaky-test-detector&lt;/code&gt; — static analysis for time deps, randomness, race conditions&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;debug-systematic&lt;/code&gt; — four-phase root-cause protocol, no fix until cause is named&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;daily-digest&lt;/code&gt; — EOD: shipped, decided, blocked, next&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dead-code-finder&lt;/code&gt; — language-aware tooling + false-positive triage&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;migration-planner&lt;/code&gt; — expand → migrate → contract → verify, kill switches each phase&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;config-doctor&lt;/code&gt; — audits settings.json, hooks, MCP servers, CLAUDE.md for issues&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;slash-command-builder&lt;/code&gt; — scaffolds new /commands from a brief&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;5 hooks&lt;/strong&gt; that automate the boring parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;session-start-brief&lt;/code&gt; — fires at session start, summarizes recent changes + git + todos&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pre-commit-quality&lt;/code&gt; — scans staged diff for secrets and red flags before any commit&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;post-edit-summary&lt;/code&gt; — per-session edit log that survives context compaction&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;stop-event-handoff&lt;/code&gt; — when session ends, appends summary to journal (anti-amnesia)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;subagent-stop-budget&lt;/code&gt; — tracks subagent invocations, warns at 75%, blocks at 100%&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;7 slash commands&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;/ship&lt;/code&gt; &lt;code&gt;/review&lt;/code&gt; &lt;code&gt;/audit&lt;/code&gt; &lt;code&gt;/triage&lt;/code&gt; &lt;code&gt;/digest&lt;/code&gt; &lt;code&gt;/orient&lt;/code&gt; &lt;code&gt;/onboard-new-repo&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1 battle-tested &lt;code&gt;CLAUDE.md&lt;/code&gt; template&lt;/strong&gt; — generalized from the operating manual that runs my own daily workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  A sample skill in full
&lt;/h2&gt;

&lt;p&gt;Here's &lt;code&gt;debug-systematic&lt;/code&gt; — the iron-law debug protocol I run every time something breaks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Debug Systematic&lt;/span&gt;

Iron law: no fixes until the root cause is named.
Most "fixes" are pattern-matched changes that suppress
one symptom and create two more.

&lt;span class="gu"&gt;## Four-phase protocol&lt;/span&gt;

&lt;span class="gu"&gt;### Phase 1 — Investigate (no hypotheses yet)&lt;/span&gt;

Goal: gather facts. Resist the urge to guess.
&lt;span class="p"&gt;
-&lt;/span&gt; Exact error message + full stack trace (copy verbatim)
&lt;span class="p"&gt;-&lt;/span&gt; Reproduction steps — every time, or intermittent?
&lt;span class="p"&gt;-&lt;/span&gt; Recent changes — last commit on affected path, last deploy time
&lt;span class="p"&gt;-&lt;/span&gt; Environment — local? staging? prod?
&lt;span class="p"&gt;-&lt;/span&gt; Frequency — first occurrence? regression? always-broken?
&lt;span class="p"&gt;-&lt;/span&gt; Related signals — logs, metrics, recent alerts

If you don't have one of these facts, ASK before proceeding.

&lt;span class="gu"&gt;### Phase 2 — Analyze (build the timeline)&lt;/span&gt;

Map what should happen vs what does happen, step by step.
Identify the EXACT point where actual diverges from expected.

&lt;span class="gu"&gt;### Phase 3 — Hypothesize (then disprove)&lt;/span&gt;

State it precisely:
"The bug is caused by X because Y happens when Z."

Design the test that would DISPROVE it.
Run it. ONE hypothesis at a time.

&lt;span class="gu"&gt;### Phase 4 — Implement (only after RC is named)&lt;/span&gt;

The fix must:
&lt;span class="p"&gt;1.&lt;/span&gt; Reference the named root cause in the commit message
&lt;span class="p"&gt;2.&lt;/span&gt; Add a test that fails without the fix and passes with it
&lt;span class="p"&gt;3.&lt;/span&gt; State the regression risk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If that's the quality bar you want for all 15 skills, the pack is for you. If it's not, don't buy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it costs money
&lt;/h2&gt;

&lt;p&gt;The whole pack is 32 files, ~60 KB zipped. I could open-source it and get GitHub stars. I'm not, because:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The free version would attract contributions of varying quality and dilute the bar. The pack works because every file has gone through real-world use.&lt;/li&gt;
&lt;li&gt;$99 once means you actually install it and use it. Free downloads sit in your &lt;code&gt;~/Downloads/&lt;/code&gt; forever.&lt;/li&gt;
&lt;li&gt;Honest economics — I want to keep building these full-time. Selling beats begging for sponsorships.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Install in 5 minutes
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Unzip into &lt;code&gt;~/.claude/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;chmod +x ~/.claude/hooks/*.sh&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Merge one block into &lt;code&gt;~/.claude/settings.json&lt;/code&gt; (full snippet in INSTALL.md)&lt;/li&gt;
&lt;li&gt;Restart Claude Code&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Full guide: INSTALL.md in the zip.&lt;/p&gt;

&lt;h2&gt;
  
  
  License
&lt;/h2&gt;

&lt;p&gt;Single-developer license. Use on every machine YOU work on. Fork and modify freely. Don't redistribute the pack itself. Team license (up to 10 devs) is a separate listing.&lt;/p&gt;

&lt;p&gt;Lifetime updates included — when I ship new skills, you get the new zip via your Gumroad library.&lt;/p&gt;

&lt;h2&gt;
  
  
  Refund
&lt;/h2&gt;

&lt;p&gt;7 days, no questions. If it doesn't compound your workflow, email me and I refund.&lt;/p&gt;




&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://manja8.gumroad.com/l/dhnzd?utm_source=devto&amp;amp;utm_medium=launch&amp;amp;utm_campaign=skills-pack-v1" rel="noopener noreferrer"&gt;Buy on Gumroad — $99 lifetime&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://skills.protodex.io" rel="noopener noreferrer"&gt;Browse the full pitch at skills.protodex.io&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Built by &lt;a href="https://dev.to/manja316"&gt;@manja316&lt;/a&gt; · I maintain &lt;a href="https://protodex.io" rel="noopener noreferrer"&gt;protodex.io&lt;/a&gt;, an index of 9,600+ MCP servers with security scores · I publish here daily.&lt;/p&gt;

&lt;p&gt;If you have a skill you'd want in v2, drop a comment. Buyer suggestions go to the front of the queue.&lt;/p&gt;

</description>
      <category>claude</category>
      <category>ai</category>
      <category>productivity</category>
      <category>devtools</category>
    </item>
    <item>
      <title>Free Polymarket Dataset on Kaggle: 13,963 Active Markets + 100K Price Sample (May 2026)</title>
      <dc:creator>manja316</dc:creator>
      <pubDate>Tue, 19 May 2026 09:53:32 +0000</pubDate>
      <link>https://forem.com/manja316/free-polymarket-dataset-on-kaggle-13963-active-markets-100k-price-sample-may-2026-4j4k</link>
      <guid>https://forem.com/manja316/free-polymarket-dataset-on-kaggle-13963-active-markets-100k-price-sample-may-2026-4j4k</guid>
      <description>&lt;p&gt;I just pushed a fresh sample of the Polymarket dataset to Kaggle. &lt;strong&gt;Free download, Apache 2.0, no email gate.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://www.kaggle.com/datasets/luciferforge/polymarket-markets-prices-sample-2026" rel="noopener noreferrer"&gt;https://www.kaggle.com/datasets/luciferforge/polymarket-markets-prices-sample-2026&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What's in it
&lt;/h2&gt;

&lt;p&gt;Two CSV files:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;Rows&lt;/th&gt;
&lt;th&gt;What it gives you&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;markets.csv&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;13,963&lt;/td&gt;
&lt;td&gt;Every currently-active Polymarket market with question, category, volume, liquidity, status, end date, slug&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;prices_sample.csv&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;100,000&lt;/td&gt;
&lt;td&gt;The most recent 15-minute price snapshots across the universe — preview of the full 10.8M-snapshot corpus&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;8 MB compressed. Loads instantly in pandas, DuckDB, Polars, or any spreadsheet.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you can build in 20 minutes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A screener.&lt;/strong&gt; Filter markets by category × volume × spread × days-to-resolution.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A volume leaderboard.&lt;/strong&gt; &lt;code&gt;markets.csv&lt;/code&gt; has &lt;code&gt;volume&lt;/code&gt;, &lt;code&gt;volumeNum&lt;/code&gt;, &lt;code&gt;volume24hr&lt;/code&gt; — rank, sort, group by category.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A "lottery ticket" finder.&lt;/strong&gt; Find low-price (&lt;code&gt;&amp;lt;$0.10&lt;/code&gt;) outcomes with non-trivial liquidity. There are hundreds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A category dashboard.&lt;/strong&gt; What's the average spread in sports vs politics vs crypto? The CSV has the data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A correlation map.&lt;/strong&gt; Even with 100K snapshots, you can compute simple correlations between sub-markets in the same event (semi-final A vs final winner, for example).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What it's NOT
&lt;/h2&gt;

&lt;p&gt;This is the &lt;strong&gt;sample&lt;/strong&gt;, not the full historical corpus.&lt;/p&gt;

&lt;p&gt;If you need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every 15-minute snapshot for 43+ days (10.8M+ rows)&lt;/li&gt;
&lt;li&gt;Orderbook depth (1.07M+ snapshots)&lt;/li&gt;
&lt;li&gt;The full SQLite database for joins&lt;/li&gt;
&lt;li&gt;Continuous updates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;...that's the &lt;a href="https://manja8.gumroad.com/l/polymarket-data?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=kaggle-sample-launch-2026-05-13&amp;amp;utm_content=upsell" rel="noopener noreferrer"&gt;$9 Polymarket Full Dataset on Gumroad&lt;/a&gt;. The Kaggle sample is the on-ramp.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it was collected
&lt;/h2&gt;

&lt;p&gt;Automated pipeline running 24/7 since March 2026:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Gamma API&lt;/strong&gt; → market metadata + prices for every active market&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CLOB API&lt;/strong&gt; → orderbook depth (top 10 levels) for top 200 markets by volume&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SQLite storage&lt;/strong&gt; with indexed timestamps for analytical queries&lt;/li&gt;
&lt;li&gt;Sample CSVs regenerated when the dataset is republished&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Source: &lt;a href="https://protodex.io" rel="noopener noreferrer"&gt;protodex.io&lt;/a&gt; — the MCP-server directory with security scores. I built the Polymarket collector to feed a separate trading bot project; the dataset is the byproduct.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why a Kaggle release at all
&lt;/h2&gt;

&lt;p&gt;Two reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;It's where the prediction-market quant crowd lives.&lt;/strong&gt; Kaggle has a dedicated "prediction markets" search niche. The other dataset I have there (&lt;a href="https://www.kaggle.com/datasets/luciferforge/polymarket-historical-prices" rel="noopener noreferrer"&gt;&lt;code&gt;polymarket-historical-prices&lt;/code&gt;&lt;/a&gt;) is sitting at 68 downloads from zero promo. Putting structured data where researchers already are has been higher-leverage than waiting for Reddit posts to go viral.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sample-to-paid funnels work.&lt;/strong&gt; If you find an edge in the free sample, paying $9 for the full corpus is an easy yes. If the sample data is unusable to you, you'd never have bought $9 anyway. Aligns incentives.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Limitations to know
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The 100K-row price sample is the &lt;strong&gt;most recent&lt;/strong&gt; 100K snapshots only — not a uniform random sample across the full 43-day window.&lt;/li&gt;
&lt;li&gt;"Active markets" here means markets that were live as of the last collection run. Resolved markets aren't included in &lt;code&gt;markets.csv&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Polymarket pulled a few markets between the snapshot and publish. Cross-check by &lt;code&gt;slug&lt;/code&gt; if you find any 404s.&lt;/li&gt;
&lt;li&gt;The dataset is updated when I refresh it — not in real time. For live data, hit the Gamma API directly (it's free and rate-limited generously).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  License
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Apache 2.0.&lt;/strong&gt; Use commercially, modify, redistribute. Credit Protodex if you publish derived work; no obligation otherwise.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd love feedback on
&lt;/h2&gt;

&lt;p&gt;If you download this and build something — even a half-finished notebook — drop a comment with what you tried. I'm watching which use cases come up so I know what the V2 corpus should prioritize (more orderbook depth? more historical reach? resolved-market history? a labeled crash-event dataset?).&lt;/p&gt;




&lt;p&gt;The dataset is live now: &lt;a href="https://www.kaggle.com/datasets/luciferforge/polymarket-markets-prices-sample-2026" rel="noopener noreferrer"&gt;Polymarket Markets + Price Sample (2026) on Kaggle&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If it's useful, an upvote on Kaggle helps it surface in the prediction-markets search — that's the only ask.&lt;/p&gt;

</description>
      <category>polymarket</category>
      <category>dataset</category>
      <category>kaggle</category>
      <category>trading</category>
    </item>
    <item>
      <title>The anatomy of a single-purpose AI tool (and why I shipped two of them this week)</title>
      <dc:creator>manja316</dc:creator>
      <pubDate>Mon, 18 May 2026 22:51:09 +0000</pubDate>
      <link>https://forem.com/manja316/the-anatomy-of-a-single-purpose-ai-tool-and-why-i-shipped-two-of-them-this-week-53f7</link>
      <guid>https://forem.com/manja316/the-anatomy-of-a-single-purpose-ai-tool-and-why-i-shipped-two-of-them-this-week-53f7</guid>
      <description>&lt;p&gt;I keep hitting the same friction with ChatGPT: I want to do ONE thing — fix this JSON, build that regex — and a chat window is the wrong shape for it. Too many turns, too much context-reset, too much typing.&lt;/p&gt;

&lt;p&gt;So I built two tools that prove a smaller hypothesis: a single-purpose page with AI as the &lt;em&gt;fallback&lt;/em&gt; beats a chat window for any task you do more than once a week.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;regexai: &lt;a href="https://regexai-six.vercel.app/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=apps-week" rel="noopener noreferrer"&gt;https://regexai-six.vercel.app/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=apps-week&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;jsonfix: &lt;a href="https://jsonfix-lake.vercel.app/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=apps-week" rel="noopener noreferrer"&gt;https://jsonfix-lake.vercel.app/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=apps-week&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The shape
&lt;/h3&gt;

&lt;p&gt;Both apps are the same shape:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Deterministic path first.&lt;/strong&gt; regexai runs your regex through native RegExp and highlights matches as you type. jsonfix calls JSON.parse and formats. No API, no latency, no cost. 95% of sessions never leave this path.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI fallback.&lt;/strong&gt; If the deterministic path fails (regex doesn't match what you want, JSON throws SyntaxError), one button hands the problem to Claude with a tight prompt. Result lands back in the same UI.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No login, no quota popup, no "sign up for more".&lt;/strong&gt; Friction kills these utilities. The cost per session is low enough that I eat it.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Why this shape wins
&lt;/h3&gt;

&lt;p&gt;Chat is bad at repeating chores because every turn re-establishes context. Single-purpose pages keep state in the URL or the textarea — paste, click, paste, click. You can do twenty in a row.&lt;/p&gt;

&lt;p&gt;ChatGPT is great for novel problems. It's wrong for the 30th time this month you needed to validate an IPv4. The tool that wins for "30th time" is the one with the lowest activation energy, and that's a bookmarked URL that does ONE thing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Architecture (small enough to fit in a tweet)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Next.js 16 App Router&lt;/li&gt;
&lt;li&gt;One client component for the UI, one route handler for the AI call&lt;/li&gt;
&lt;li&gt;@anthropic-ai/sdk on the server, claude-haiku-4-5 for speed and cost&lt;/li&gt;
&lt;li&gt;Edge runtime so cold starts don't matter&lt;/li&gt;
&lt;li&gt;Vercel analytics, nothing else&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's it. No database. No auth. No queue. The state of the app is whatever's in the textarea. If the tab closes, the work is gone — and that's correct, because the user is solving a 30-second problem, not building a project.&lt;/p&gt;

&lt;h3&gt;
  
  
  The prompt that does the heavy lifting
&lt;/h3&gt;

&lt;p&gt;For jsonfix:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You are a JSON repair tool. The user pasted text they believe should be valid JSON but it isn't. Return the corrected JSON and a one-line explanation of what was broken. Output as &lt;code&gt;{"fixed": "...", "explanation": "..."}&lt;/code&gt;. Do not invent fields. Preserve all keys and values; only fix syntax.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For regexai:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You are a regex builder. Given the user's description, return &lt;code&gt;{"pattern": "...", "flags": "g", "explanation": "...", "testString": "..."}&lt;/code&gt;. Use ECMAScript regex syntax (no PCRE-only features). Generate a testString that demonstrates a match.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Both prompts are pinned: model name, system prompt, schema. No few-shot examples — the schema in the prompt is enough at this scope.&lt;/p&gt;

&lt;h3&gt;
  
  
  What I'd build next in this shape
&lt;/h3&gt;

&lt;p&gt;Tools that are great candidates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;cron expression builder&lt;/li&gt;
&lt;li&gt;SQL query explainer (paste a query, get the plain English)&lt;/li&gt;
&lt;li&gt;diff explainer (paste two blobs, get a semantic summary)&lt;/li&gt;
&lt;li&gt;HTTP request replayer (paste curl, get fetch / Python requests / etc.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The pattern is the same every time: deterministic path, AI fallback, no login, one job per page.&lt;/p&gt;

&lt;h3&gt;
  
  
  Try them
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;regexai: &lt;a href="https://regexai-six.vercel.app/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=apps-week" rel="noopener noreferrer"&gt;https://regexai-six.vercel.app/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=apps-week&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;jsonfix: &lt;a href="https://jsonfix-lake.vercel.app/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=apps-week" rel="noopener noreferrer"&gt;https://jsonfix-lake.vercel.app/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=apps-week&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you build one in the same shape, drop the link — happy to feature it.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>nextjs</category>
      <category>ai</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Every Indian GST invoice generator forces signup. I built one that doesn't.</title>
      <dc:creator>manja316</dc:creator>
      <pubDate>Mon, 18 May 2026 10:51:53 +0000</pubDate>
      <link>https://forem.com/manja316/every-indian-gst-invoice-generator-forces-signup-i-built-one-that-doesnt-2hj7</link>
      <guid>https://forem.com/manja316/every-indian-gst-invoice-generator-forces-signup-i-built-one-that-doesnt-2hj7</guid>
      <description>&lt;p&gt;I needed to send one GST invoice last week. Took me 25 minutes.&lt;/p&gt;

&lt;p&gt;Vyapar wanted me to sign up. Zoho Books wanted me to sign up. Tally wanted me to install desktop software and walked me through a 3-screen wizard. The Indian government's e-invoice portal needed an aadhaar OTP and a separate "tax payer login."&lt;/p&gt;

&lt;p&gt;I just needed: enter buyer GSTIN + line items → PDF.&lt;/p&gt;

&lt;p&gt;So I built it. Single page, runs in your browser, generates a compliant tax invoice in 60 seconds. Free, open source, no signup, no analytics on your form data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Live demo:&lt;/strong&gt; &lt;a href="https://gst.protodex.io?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=gst-launch-week" rel="noopener noreferrer"&gt;https://gst.protodex.io&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's what's actually in it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a GST invoice needs (the bureaucratic part)
&lt;/h2&gt;

&lt;p&gt;Rule 46 of the CGST Rules lists 16 mandatory fields. The interesting ones from a code perspective:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;GSTIN of supplier and recipient&lt;/strong&gt; (15 chars, format: &lt;code&gt;27ZYXWV9876E1A2&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HSN/SAC code per line item&lt;/strong&gt; — 4 digits if your turnover ≤ ₹5cr, 6 digits if more&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GST split: CGST + SGST if same state, IGST if different state&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Place of supply&lt;/strong&gt; — determines the split logic above&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The split is where most calculators get it wrong. Naive implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tax&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;gstPercent&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;100&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;cgst&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tax&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// ← WRONG when seller != buyer state&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sgst&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tax&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Correct implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sameState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sellerStateCode&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;buyerStateCode&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;tax&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;gstPercent&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;100&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;cgst&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sameState&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;tax&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&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;sgst&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sameState&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;tax&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&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;igst&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sameState&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;tax&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your seller is in Karnataka (KA) and your buyer is in Maharashtra (MH), it's IGST — a single tax that the Central government collects and redistributes. Same state: CGST + SGST, split equally between Central and State.&lt;/p&gt;

&lt;p&gt;I bundled the top 40 HSN/SAC codes most SMBs and freelancers use (software dev &lt;code&gt;998314&lt;/code&gt;, hosting &lt;code&gt;998433&lt;/code&gt;, legal &lt;code&gt;998212&lt;/code&gt;, retail goods classes &lt;code&gt;8471&lt;/code&gt; for computers, &lt;code&gt;6109&lt;/code&gt; for T-shirts, etc.). Full official list is ~150k codes at &lt;a href="https://cbic-gst.gov.in/" rel="noopener noreferrer"&gt;cbic-gst.gov.in&lt;/a&gt; if you need an obscure category.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the PDF gets generated (no server)
&lt;/h2&gt;

&lt;p&gt;The PDF is built entirely client-side using &lt;a href="https://github.com/parallax/jsPDF" rel="noopener noreferrer"&gt;jsPDF&lt;/a&gt; + the &lt;code&gt;jspdf-autotable&lt;/code&gt; plugin. The whole pipeline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;jsPDF&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;jspdf&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;jspdf-autotable&lt;/span&gt;&lt;span class="dl"&gt;'&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;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;jsPDF&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;unit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;a4&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;TAX INVOICE&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;105&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;center&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;autoTable&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;startY&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;head&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Description&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;HSN&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Qty&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Rate&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Total&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;lineItems&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;toRow&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;invoice.pdf&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No server, no upload, no API. Your invoice data never leaves the tab. Open DevTools → Network → you'll see zero requests after page load.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I deliberately didn't build
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GSTR-1 filing&lt;/strong&gt; — that's a monthly compliance feature. Use Zoho/Vyapar.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;E-invoice / IRN generation&lt;/strong&gt; — mandatory only if turnover &amp;gt; ₹5cr. The handful of buyers above that threshold already use enterprise tools.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recurring invoices&lt;/strong&gt; — if you need this, you need accounting software anyway.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Server-side persistence&lt;/strong&gt; — invoices auto-save to your browser's &lt;code&gt;localStorage&lt;/code&gt;. Refresh-safe, machine-bound. Switch machines = re-enter once.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What I learned about Indian tax software
&lt;/h2&gt;

&lt;p&gt;It's a market where the dominant tools (Tally, Zoho, Vyapar) compete on feature breadth and SaaS pricing, but they all skip the use case I had: &lt;strong&gt;"I send one invoice every other month and just want a PDF."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That's a long tail problem — too small for SaaS, too irregular for a Tally install, too compliance-heavy for a Word template. So everyone falls back to copy-pasting an Excel template they downloaded from a forum 3 years ago. Or hires a CA for what's a 60-second task.&lt;/p&gt;

&lt;p&gt;The free no-signup tier is the right shape for that user.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;Things on the roadmap if there's interest:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Logo upload&lt;/strong&gt; (currently the PDF is text-only)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Save invoice history&lt;/strong&gt; locally (right now &lt;code&gt;localStorage&lt;/code&gt; keeps the form, not the past PDFs)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CSV export of line items&lt;/strong&gt; for accounting handoff&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;e-Way bill generator&lt;/strong&gt; for goods movement &amp;gt; ₹50k (separate compliance category)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let me know which of these you'd actually use. If it's just "I need logo upload," I'll ship that this week.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Source / try it:&lt;/strong&gt; &lt;a href="https://gst.protodex.io?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=gst-launch-week" rel="noopener noreferrer"&gt;https://gst.protodex.io&lt;/a&gt;&lt;br&gt;
This is part of &lt;a href="https://protodex.io?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=gst-launch-week" rel="noopener noreferrer"&gt;protodex.io&lt;/a&gt; — a small set of free utilities focused on Indian compliance + global dev work. Sister sites: &lt;a href="https://property.protodex.io?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=gst-launch-week" rel="noopener noreferrer"&gt;property.protodex.io&lt;/a&gt; (Indian property total-cost calculator), &lt;a href="https://cron.protodex.io?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=gst-launch-week" rel="noopener noreferrer"&gt;cron.protodex.io&lt;/a&gt; (cron expression explainer with systemd timer output), &lt;a href="https://airdrop.protodex.io?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=gst-launch-week" rel="noopener noreferrer"&gt;airdrop.protodex.io&lt;/a&gt; (free crypto airdrop scanner — relevant if you do any crypto on the side and need to track Schedule VDA disclosure).&lt;/p&gt;

</description>
      <category>india</category>
      <category>opensource</category>
      <category>webdev</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Week 1 of pnl-truthteller: 22 pypi downloads, 0 stars, and one real bug report</title>
      <dc:creator>manja316</dc:creator>
      <pubDate>Mon, 18 May 2026 01:33:58 +0000</pubDate>
      <link>https://forem.com/manja316/week-1-of-pnl-truthteller-22-pypi-downloads-0-stars-and-one-real-bug-report-3ba2</link>
      <guid>https://forem.com/manja316/week-1-of-pnl-truthteller-22-pypi-downloads-0-stars-and-one-real-bug-report-3ba2</guid>
      <description>&lt;p&gt;A week ago I shipped &lt;a href="https://github.com/LuciferForge/pnl-truthteller" rel="noopener noreferrer"&gt;&lt;code&gt;pnl-truthteller&lt;/code&gt;&lt;/a&gt; — a one-command auditor that reads a Polymarket wallet's on-chain fills and compares them against what the bot's own DB &lt;em&gt;thinks&lt;/em&gt; it earned. The thesis: most trading bots overstate P&amp;amp;L by 5–15% because they log the post-order intent, not the matched fill.&lt;/p&gt;

&lt;p&gt;I wrote up the launch &lt;a href="https://dev.to/manja316/your-polymarket-bot-is-lying-to-you-about-pl-i-built-a-free-auditor-heres-what-i-found-3b9a"&gt;in this article&lt;/a&gt;. This is the follow-up. Same project, one week later, all numbers.&lt;/p&gt;

&lt;h2&gt;
  
  
  The numbers (week 1)
&lt;/h2&gt;

&lt;p&gt;Pulled from APIs at write time, not eyeballed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;pypi downloads — last 7d:&lt;/strong&gt; 22&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;pypi downloads — last 30d:&lt;/strong&gt; 276&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;github stars:&lt;/strong&gt; 0&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;github forks:&lt;/strong&gt; 0&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;github open issues:&lt;/strong&gt; 0&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;gumroad sales of the optional dataset:&lt;/strong&gt; 0&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Discussions threads opened by real users:&lt;/strong&gt; 1 (about partial-fill dedup — the genuinely hard part of the implementation)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's a real number row, not a vibe. If you've shipped indie open-source you know how those numbers feel: not zero, not exciting, ambiguous. The honest read is "evidence the audience exists, no evidence it's a magnet yet."&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'm calling success vs. noise
&lt;/h2&gt;

&lt;p&gt;I committed before launch to a kill rule: four flat weeks in a row across all five channels and the project gets retired. Week 1 doesn't trigger it (no trend to flat against yet), but I'm logging the baseline publicly so I can't fudge it later.&lt;/p&gt;

&lt;p&gt;What I'm watching:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Returning installs.&lt;/strong&gt; 276/month on a tool with no marketing is mostly first-touch curiosity. The question is whether anyone runs it twice. pypistats doesn't expose that, so I'll proxy it with GitHub repo unique-visitor traffic and Discussions activity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Slippage Index contributions.&lt;/strong&gt; I asked for anonymized audit outputs to build a cross-operator dataset. n=1 so far (me). Goal n=10.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The category-bug claim landing.&lt;/strong&gt; I pulled a random Polymarket wallet off the public CLOB and ran the audit cold — DB-equivalent +$32, on-chain −$30. Different bot, different strategy, same gap shape. If others repro this on their own wallets, the tool has a moat (it's not just my problem). That signal is the most valuable.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What I'd do differently if shipping week-1 again
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Lead with the random-wallet audit, not the launch announcement.&lt;/strong&gt; The category-bug evidence is more interesting than "I built a tool." I buried it in section 4 of the launch post. Should've been the title.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Default the CLI to &lt;code&gt;--output=stdout&lt;/code&gt; instead of a markdown file.&lt;/strong&gt; A surprising number of people opened the tool, saw it wanted a path, and bounced. Quietly painful UX miss.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cut the optional Gumroad bundle from week 1.&lt;/strong&gt; Free tool + paid dataset on day-one creates an "is this a sales funnel?" smell. Should've spent week 1 building trust and dropped the dataset week 3.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What's in the dataset (if you actually want it)
&lt;/h2&gt;

&lt;p&gt;Five real Polymarket wallets, fully audited: per-trade DB-vs-onchain delta, slippage attribution (price vs. partial-fill vs. gas), and the raw CLOB fills as CSV. It's the input I used to validate the tool wasn't just confirming my own bias.&lt;/p&gt;

&lt;p&gt;If that's useful — &lt;a href="https://lucifer.gumroad.com/l/pnl-truthteller?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=pnl-truthteller-week1-retro-2026-05-17" rel="noopener noreferrer"&gt;it's $19 on Gumroad&lt;/a&gt;. One-time, no subscription. The tool itself stays free and MIT.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code, and the only ask
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;pnl-truthteller
pnl-truthteller &lt;span class="nt"&gt;--wallet&lt;/span&gt; 0xYourProxyWallet &lt;span class="nt"&gt;--output&lt;/span&gt; report.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you run any bot on a CLOB-style venue (Polymarket, Kalshi, dydx, GMX-perps) and audit your own wallet, the only thing I'd ask is: drop the &lt;em&gt;one number&lt;/em&gt; (P&amp;amp;L gap %) in &lt;a href="https://github.com/LuciferForge/pnl-truthteller/discussions/3" rel="noopener noreferrer"&gt;Discussion #3&lt;/a&gt;. Zero is fine. Negative is also fine. I just want the distribution.&lt;/p&gt;

&lt;p&gt;Week 2 retro lands next Saturday with these same metrics + week-over-week delta.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>python</category>
      <category>postmortem</category>
      <category>datascience</category>
    </item>
    <item>
      <title>Give Claude (or Cursor) live Polymarket prediction-market data with one MCP server</title>
      <dc:creator>manja316</dc:creator>
      <pubDate>Tue, 12 May 2026 02:19:19 +0000</pubDate>
      <link>https://forem.com/manja316/give-claude-or-cursor-live-polymarket-prediction-market-data-with-one-mcp-server-58ch</link>
      <guid>https://forem.com/manja316/give-claude-or-cursor-live-polymarket-prediction-market-data-with-one-mcp-server-58ch</guid>
      <description>&lt;h1&gt;
  
  
  Give Claude (or Cursor) live Polymarket prediction-market data with one MCP server
&lt;/h1&gt;

&lt;p&gt;Polymarket has a public API. Your AI agent — Claude Desktop, Cursor, Cline — does not speak it. So when you ask "what prediction markets just crashed?" the agent either makes something up, scrapes a stale wiki, or politely declines.&lt;/p&gt;

&lt;p&gt;I got tired of that. &lt;code&gt;polymarket-mcp-pro&lt;/code&gt; is a &lt;a href="https://modelcontextprotocol.io" rel="noopener noreferrer"&gt;Model Context Protocol&lt;/a&gt; server that exposes seven read-only tools to any MCP-compatible client. The agent can list markets by volume, pull historical price snapshots, find recent crashes, and inspect order-book depth — all over a structured JSON tool surface, no HTTP code in your prompts.&lt;/p&gt;

&lt;p&gt;It's free, open source, and backed by a real dataset (not a toy demo).&lt;/p&gt;

&lt;h2&gt;
  
  
  The numbers behind it
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Stats refreshed 2026-05-25 — corpus now spans 58 days of continuous indexing. Markets: 13,964 → 15,355 (+10%); price snapshots: 10.8M → 15.1M (+40%) since first publish on 2026-05-12.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The MCP server is just the agent-facing skin. The data layer behind it is &lt;code&gt;api.protodex.io&lt;/code&gt;, which indexes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;15,355&lt;/strong&gt; Polymarket markets&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;15.1M+&lt;/strong&gt; price snapshots (one every 15 minutes per active market)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;$11.7B+&lt;/strong&gt; cumulative volume covered&lt;/li&gt;
&lt;li&gt;Refreshed continuously, served via a stable REST surface&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you ask the agent "show me markets that dropped 15% in the last 4 hours," it's running against that whole corpus, not a 10-market sample.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install in 30 seconds
&lt;/h2&gt;

&lt;p&gt;If you have &lt;a href="https://docs.astral.sh/uv/" rel="noopener noreferrer"&gt;&lt;code&gt;uv&lt;/code&gt;&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uvx &lt;span class="nt"&gt;--from&lt;/span&gt; polymarket-mcp-pro polymarket-mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or, classic pip:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;polymarket-mcp-pro
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The PyPI distribution name is &lt;code&gt;polymarket-mcp-pro&lt;/code&gt; because the bare &lt;code&gt;polymarket-mcp&lt;/code&gt; was taken by an unrelated package. The CLI and import path both stay &lt;code&gt;polymarket_mcp&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wire it into Claude Desktop
&lt;/h2&gt;

&lt;p&gt;Edit &lt;code&gt;~/Library/Application Support/Claude/claude_desktop_config.json&lt;/code&gt; (macOS) or &lt;code&gt;%APPDATA%\Claude\claude_desktop_config.json&lt;/code&gt; (Windows):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"polymarket"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"uvx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"polymarket-mcp"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restart Claude Desktop. The &lt;code&gt;polymarket&lt;/code&gt; tools appear in the tool picker.&lt;/p&gt;

&lt;p&gt;Cursor: same JSON block under Settings → Features → MCP Servers. Cline, Continue, any other MCP client over stdio: same command.&lt;/p&gt;

&lt;h2&gt;
  
  
  The seven tools
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get_stats&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Dataset-wide counts and freshness&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;list_markets&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Paginated list with category, search, sort&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get_market&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Single market detail with CLOB token ids&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get_prices&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Historical price snapshots for a Yes/No outcome&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get_crashes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Markets that dropped &amp;gt;= N% in the last H hours&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get_categories&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Category breakdown with market counts and volume&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get_orderbook&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Live bid/ask depth for a CLOB token id&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Everything is read-only. There is no order placement, no signing, no wallet. The agent observes; you decide.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I actually use it for
&lt;/h2&gt;

&lt;p&gt;Three workflows, real not hypothetical:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Crash scanner as a morning routine.&lt;/strong&gt; "Run &lt;code&gt;get_crashes&lt;/code&gt; with threshold 15% over 4 hours, filter to volume &amp;gt; $100k, summarize the top 5." The agent comes back with a structured list and a paragraph each on what each market is. Most days zero matches. Some days something interesting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Researching a news event.&lt;/strong&gt; When something happens in the world that has prediction markets — election, ruling, central-bank meeting — I ask the agent to pull every related market plus its 24h price action. The agent picks the right markets via search; the data is fresh because it's queried at prompt time, not from training data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Building strategy notes.&lt;/strong&gt; I keep a Markdown file of strategy hypotheses. The agent appends "current state" sections by calling the tools — "as of this query, the resolution market for X is trading at Y with Z volume in the last day." No copy-paste from a Polymarket browser tab.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this isn't just a thin wrapper
&lt;/h2&gt;

&lt;p&gt;A thin wrapper would forward the agent's question to Polymarket's CLOB API and call it a day. Two problems with that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The CLOB API is rate-limited and returns raw orderbook diffs. An agent burning tokens decoding diff sequences is wasteful.&lt;/li&gt;
&lt;li&gt;There is no historical price endpoint on the live API. If you want price 18 hours ago, you have to maintain your own snapshot pipeline.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;polymarket-mcp-pro&lt;/code&gt; talks to &lt;code&gt;api.protodex.io&lt;/code&gt;, which has been running that snapshot pipeline since early 2026. The historical depth (15.1M+ points) is the thing the agent gets that no thin-wrapper alternative provides.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's coming in v0.2
&lt;/h2&gt;

&lt;p&gt;A WebSocket subscription transport. Right now &lt;code&gt;get_crashes&lt;/code&gt; is poll-based — the agent asks, the server returns the current snapshot. v0.2 will let the agent subscribe to a stream and get pushed updates when a crash threshold is breached, without re-asking. The MCP spec supports this natively; it just needs glue code on my end.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get the historical dataset too
&lt;/h2&gt;

&lt;p&gt;The MCP server gives your agent live access. If you want the full historical corpus as a flat CSV for offline analysis, backtests, or building your own thing on top — &lt;a href="https://manja8.gumroad.com/l/polymarket-data?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=polymarket-mcp-week22" rel="noopener noreferrer"&gt;that's available here&lt;/a&gt;. 15.1M+ price records, 15,355 markets, $11.7B+ volume, one-time purchase.&lt;/p&gt;

&lt;p&gt;The MCP server stays free either way. The dataset is the optional commercial layer for people who want bulk access without hammering an API. (For reference: &lt;code&gt;polymarket-mcp-pro&lt;/code&gt; is currently doing ~18 weekly PyPI downloads with zero promotion budget.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/LuciferForge/polymarket-mcp" rel="noopener noreferrer"&gt;https://github.com/LuciferForge/polymarket-mcp&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PyPI:&lt;/strong&gt; &lt;a href="https://pypi.org/project/polymarket-mcp-pro/" rel="noopener noreferrer"&gt;https://pypi.org/project/polymarket-mcp-pro/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sister project (free auditor for your existing trades):&lt;/strong&gt; &lt;a href="https://github.com/LuciferForge/pnl-truthteller" rel="noopener noreferrer"&gt;https://github.com/LuciferForge/pnl-truthteller&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Issues / requests:&lt;/strong&gt; drop them on the repo&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you build something with this, I want to hear about it. Open a GitHub Discussion on the repo or comment below.&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>ai</category>
      <category>python</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Your Polymarket Bot Is Lying To You About P&amp;L. I Built A Free Auditor. Here's What I Found.</title>
      <dc:creator>manja316</dc:creator>
      <pubDate>Mon, 11 May 2026 10:51:01 +0000</pubDate>
      <link>https://forem.com/manja316/your-polymarket-bot-is-lying-to-you-about-pl-i-built-a-free-auditor-heres-what-i-found-3b9a</link>
      <guid>https://forem.com/manja316/your-polymarket-bot-is-lying-to-you-about-pl-i-built-a-free-auditor-heres-what-i-found-3b9a</guid>
      <description>&lt;h1&gt;
  
  
  Your Polymarket Bot Is Lying To You About P&amp;amp;L. I Built A Free Auditor. Here's What I Found.
&lt;/h1&gt;

&lt;p&gt;If your Polymarket bot tells you it's up $34 on the week, there's a decent chance your wallet is actually &lt;em&gt;down&lt;/em&gt; $90. The CLOB doesn't fill the way your &lt;code&gt;post_order&lt;/code&gt; response suggests it does, and most bots never reconcile the difference.&lt;/p&gt;

&lt;p&gt;I ran into this problem on my own crash-recovery bot, lost real money to it, and built a tool to find the gap. It's a one-line install. You give it a wallet address and it tells you how much your fill slippage is actually costing you.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;pnl-truthteller
pnl-truthteller &lt;span class="nt"&gt;--wallet&lt;/span&gt; 0xYourPolymarketProxy &lt;span class="nt"&gt;--output&lt;/span&gt; report.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read-only. No API keys. No private key. Wallet address only.&lt;/p&gt;

&lt;h2&gt;
  
  
  The actual gap, on my own bot
&lt;/h2&gt;

&lt;p&gt;My bot logged every closed trade into SQLite with a &lt;code&gt;theoretical_pnl&lt;/code&gt; column computed from &lt;code&gt;(exit_price - entry_price) × shares&lt;/code&gt;. After 320 trades it claimed +$34.31. The on-chain reality:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;th&gt;Trades&lt;/th&gt;
&lt;th&gt;DB-reported P&amp;amp;L&lt;/th&gt;
&lt;th&gt;On-chain P&amp;amp;L&lt;/th&gt;
&lt;th&gt;Hidden slippage&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;My bot&lt;/td&gt;
&lt;td&gt;320&lt;/td&gt;
&lt;td&gt;$+34.31&lt;/td&gt;
&lt;td&gt;$-90.72&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$-125.03&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Random wallet &lt;code&gt;0x1417…&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;65&lt;/td&gt;
&lt;td&gt;$+32.36&lt;/td&gt;
&lt;td&gt;$-30.29&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$-62.66&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The second row is the part that matters. I picked a stranger's wallet off the public CLOB feed, reconstructed what their bot's DB &lt;em&gt;would&lt;/em&gt; have said, and got the same shape: small positive reported, real negative on-chain, a multi-bps gap that nobody is watching.&lt;/p&gt;

&lt;p&gt;That gap is the entire thesis of this tool. It's not just my bot. It's the default behavior of every bot that records P&amp;amp;L on order submission instead of fill confirmation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it happens
&lt;/h2&gt;

&lt;p&gt;Polymarket's CLOB fills in stages. FOK rejects, partial fills, sweep retries at deeper prices, dust left behind, idempotency issues on retry loops. If your code is shaped like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;success&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;INSERT INTO trades (theoretical_pnl, ...) VALUES (?, ...)&lt;/span&gt;&lt;span class="sh"&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;…then &lt;code&gt;theoretical_pnl&lt;/code&gt; is computed against the &lt;em&gt;intended&lt;/em&gt; fill price. The actual fills can be 5–25% worse, especially on sweep retries. Your DB shows the intention. The chain shows the execution. The delta is your real cost of doing business.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the report actually looks like
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Slippage Report — 2026-04-28T14:30:00+00:00&lt;/span&gt;

&lt;span class="gu"&gt;## TL;DR&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Closed trades total: 308
&lt;span class="p"&gt;-&lt;/span&gt; Lifetime theoretical P&amp;amp;L: +$33.49
&lt;span class="p"&gt;-&lt;/span&gt; Lifetime actual P&amp;amp;L (on-chain fills): -$89.01
&lt;span class="p"&gt;-&lt;/span&gt; Total slippage cost: -$122.50 (-365.8% of theoretical)
&lt;span class="p"&gt;-&lt;/span&gt; Trades with stranded dust on-chain: 31 (47.3 shares dust)

&lt;span class="gu"&gt;## By exit reason&lt;/span&gt;
| Reason | n | Theoretical | Actual | Slippage |
|---|---|---|---|---|
| TIMEOUT | 142 | -$18.00 | -$84.50 | -$66.50 |
| TARGET | 71 | +$28.40 | +$22.10 | -$6.30 |
| RECOVERY_TRAILED | 50 | +$15.20 | +$12.40 | -$2.80 |
| STOP | 39 | +$7.89 | +$0.99 | -$6.90 |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The "by exit reason" breakdown is the most actionable column. In my case TIMEOUT exits were the entire bleed: my bot was waiting too long to flatten and the book moved against it before each forced close. Once I knew that, the fix was an hour of work. Without the report I would have kept blaming "noise."&lt;/p&gt;

&lt;h2&gt;
  
  
  How the matching works (the only non-obvious part)
&lt;/h2&gt;

&lt;p&gt;For each closed trade the tool:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Finds the actual BUY fills by matching &lt;code&gt;token_id&lt;/code&gt; + a timestamp window, deduplicated by &lt;code&gt;orderID&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Finds the actual SELL fills that closed the position the same way.&lt;/li&gt;
&lt;li&gt;Computes &lt;code&gt;theoretical = (exit_price - entry_price) × shares&lt;/code&gt; (what the bot thinks).&lt;/li&gt;
&lt;li&gt;Computes &lt;code&gt;actual = sum(sell_takingAmount) - sum(buy_makingAmount)&lt;/code&gt; (what the chain says).&lt;/li&gt;
&lt;li&gt;Slippage = actual - theoretical. Negative = your fill ladder walked the book down.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The dedup-by-&lt;code&gt;orderID&lt;/code&gt; step is the one most rolled-your-own scripts miss. Sweep retries — where your bot tries 5%, 15%, 25% off the reference price — frequently log the same &lt;code&gt;orderID&lt;/code&gt; multiple times if you call &lt;code&gt;post_order&lt;/code&gt; from a retry loop without idempotency checks. Without dedup you double-count proceeds and your slippage looks fine when it isn't.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three input modes, depending on how you log
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Wallet address only&lt;/strong&gt; (zero setup):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnl-truthteller &lt;span class="nt"&gt;--wallet&lt;/span&gt; 0xYourProxyAddress &lt;span class="nt"&gt;--output&lt;/span&gt; report.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pulls every fill for the wallet from Polymarket's public CLOB API, groups by token + direction, produces the report. Works on any wallet — yours, a competitor's, a random one off the feed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. From your bot's SQLite&lt;/strong&gt; (if you've been logging raw CLOB responses):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnl-truthteller &lt;span class="nt"&gt;--sqlite&lt;/span&gt; ~/bot/trades.db &lt;span class="se"&gt;\&lt;/span&gt;
                &lt;span class="nt"&gt;--positions&lt;/span&gt; ~/bot/positions.json &lt;span class="se"&gt;\&lt;/span&gt;
                &lt;span class="nt"&gt;--output&lt;/span&gt; report.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expects a &lt;code&gt;live_trades&lt;/code&gt; table with &lt;code&gt;token_id, side, timestamp, raw_response&lt;/code&gt;. The &lt;code&gt;raw_response&lt;/code&gt; should be the JSON string returned by &lt;code&gt;client.post_order()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. From JSONL&lt;/strong&gt; (for non-Python stacks):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnl-truthteller &lt;span class="nt"&gt;--trades&lt;/span&gt; trades.jsonl &lt;span class="nt"&gt;--fills&lt;/span&gt; fills.jsonl &lt;span class="nt"&gt;--output&lt;/span&gt; report.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What this is not
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;It does not execute trades. Read-only.&lt;/li&gt;
&lt;li&gt;It does not need your private key. Wallet address only.&lt;/li&gt;
&lt;li&gt;It does not store your data anywhere. Local file in, local file out.&lt;/li&gt;
&lt;li&gt;It does not promise to fix your bot. It finds the gap; you fix the bot.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Get it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;PyPI&lt;/strong&gt;: &lt;code&gt;pip install pnl-truthteller&lt;/code&gt; — &lt;a href="https://pypi.org/project/pnl-truthteller/" rel="noopener noreferrer"&gt;pypi.org/project/pnl-truthteller&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/LuciferForge/pnl-truthteller" rel="noopener noreferrer"&gt;LuciferForge/pnl-truthteller&lt;/a&gt; — stars welcome, issues more welcome&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Polymarket dataset I tested it against&lt;/strong&gt; (13,964 markets, 10.8M price records, $11.7B+ volume) is on Gumroad: &lt;a href="https://manja8.gumroad.com/l/polymarket-data?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=pnl-truthteller-week-2026-05-11" rel="noopener noreferrer"&gt;manja8.gumroad.com/l/polymarket-data?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=pnl-truthteller-week-2026-05-11&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're running anything on the Polymarket CLOB and have not reconciled DB vs chain in the last 30 days, run this against your wallet today. The 30-second answer is worth more than the next feature you were going to add.&lt;/p&gt;

</description>
      <category>python</category>
      <category>trading</category>
      <category>crypto</category>
      <category>opensource</category>
    </item>
    <item>
      <title>I Built a Free Polymarket Screener with 2,001 Market Pages — Static, Open Source, No Account</title>
      <dc:creator>manja316</dc:creator>
      <pubDate>Sun, 10 May 2026 07:28:50 +0000</pubDate>
      <link>https://forem.com/manja316/i-built-a-free-polymarket-screener-with-2001-market-pages-static-open-source-no-account-17g3</link>
      <guid>https://forem.com/manja316/i-built-a-free-polymarket-screener-with-2001-market-pages-static-open-source-no-account-17g3</guid>
      <description>&lt;h1&gt;
  
  
  I Built a Free Polymarket Screener with 2,001 Market Pages — Static, Open Source, No Account
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; I built a free Polymarket prediction-market screener with &lt;strong&gt;2,001 individual market pages&lt;/strong&gt; — top movers, highest volume, crash signals, by category. Live at &lt;a href="https://luciferforge.github.io/polyscope/" rel="noopener noreferrer"&gt;luciferforge.github.io/polyscope&lt;/a&gt;. No login, no account, no rate-limited API calls. Built from the same 14.6M-row dataset I use for my own bot.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Updated 2026-05-25 — numbers verified from source&lt;/strong&gt;: 13,963 markets tracked, 10.8M+ price snapshots, 43+ days of data. &lt;a href="https://github.com/LuciferForge/polyscope/discussions/1" rel="noopener noreferrer"&gt;Community roadmap discussion →&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The problem with Polymarket's UI
&lt;/h2&gt;

&lt;p&gt;Polymarket has thousands of active markets. Their official UI shows a curated front page (politics + sports trending), but if you want to see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The 50 highest-volume markets right now&lt;/li&gt;
&lt;li&gt;The biggest probability-moves in the last 24h&lt;/li&gt;
&lt;li&gt;Markets in a specific category (geopolitics, crypto, weather, etc.)&lt;/li&gt;
&lt;li&gt;Markets that just crashed below their 7-day high&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;…you have to click into each market individually, or use their JSON API and write your own dashboard.&lt;/p&gt;

&lt;p&gt;I wrote my own dashboard 6 months ago for personal use. Today I'm making it public.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's at luciferforge.github.io/polyscope
&lt;/h2&gt;

&lt;p&gt;A static-site screener with &lt;strong&gt;2,001 individual market pages&lt;/strong&gt; + 10 category indexes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;By category&lt;/strong&gt;: politics, sports, crypto, geopolitics, economics, entertainment, weather, science_tech, pop_culture, other&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Per market&lt;/strong&gt;: question, current Yes/No price, 7-day price chart, total dollar volume, current liquidity, end date, resolution status&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sortable indexes&lt;/strong&gt;: by volume, by liquidity, by 24h price change&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Top movers + crash signals&lt;/strong&gt; front page: the 50 markets with the biggest moves and the 50 closest to their 7-day low&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All static HTML. No JS framework, no database call on page load. Renders in &amp;lt;100 ms. Indexed by Google.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why static
&lt;/h2&gt;

&lt;p&gt;Polymarket's own dashboard is rate-limited and requires JS. A static site means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Search engines can index every individual market page&lt;/li&gt;
&lt;li&gt;Anyone can mirror the site, fork it, run it themselves&lt;/li&gt;
&lt;li&gt;No infrastructure cost — it's GitHub Pages&lt;/li&gt;
&lt;li&gt;The methodology is auditable: the generator script + the SQL queries are in the repo&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How it's built
&lt;/h2&gt;

&lt;p&gt;The site is generated from a 10.8M-snapshot Polymarket price database I've been collecting since mid-April 2026. Every 15 minutes the collector pulls Gamma + CLOB APIs, deduplicates, and inserts into SQLite. The screener generation script reads the DB, picks the 2,000 most-active markets, and renders the static pages.&lt;/p&gt;

&lt;p&gt;If you want to build something like this yourself, the dataset is for sale on Gumroad: &lt;strong&gt;&lt;a href="https://manja8.gumroad.com/l/agyjd?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=polyscope-week22" rel="noopener noreferrer"&gt;10.8M+ price snapshots, 13,963 markets, 43+ days, $9 — SQLite + CSV&lt;/a&gt;&lt;/strong&gt;. Same data the screener runs on.&lt;/p&gt;

&lt;p&gt;If you want any of those, two adjacent products:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Free CLI to audit your own Polymarket trading P&amp;amp;L&lt;/strong&gt;: &lt;a href="https://pypi.org/project/pnl-truthteller/" rel="noopener noreferrer"&gt;pnl-truthteller on PyPI&lt;/a&gt;. Open source.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Buyable bot template&lt;/strong&gt; (the one I trade with): &lt;a href="https://manja8.gumroad.com/l/Polycrashbot?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=polyscope-week22" rel="noopener noreferrer"&gt;Polymarket Crash Recovery Bot — 280 trades, 80% WR&lt;/a&gt;. $39 on Gumroad.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Everything's open source: &lt;a href="https://github.com/LuciferForge/polyscope" rel="noopener noreferrer"&gt;github.com/LuciferForge/polyscope&lt;/a&gt;. Discussions are open — what screener views would you actually use? &lt;a href="https://github.com/LuciferForge/polyscope/discussions/1" rel="noopener noreferrer"&gt;Vote on the roadmap →&lt;/a&gt;. Pull requests welcome.&lt;/p&gt;

&lt;p&gt;The screener regenerates from a daily DB snapshot. Found a market that should be on the trending page but isn't? Open an issue with the market_id.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built by &lt;a href="https://dev.to/manja316"&gt;@manja316&lt;/a&gt;. I run a Polymarket trading desk + a prediction market dataset business. Free tools for the same audience that buys the data.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>polymarket</category>
      <category>trading</category>
      <category>showdev</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
