Why Most AI Coding Tool Comparisons Still Miss the Workflow Layer

Most AI coding tool comparisons are still too shallow.

They compare things like:

• features
• models
• pricing
• UI polish
• one-shot code generation quality

Those things matter a little.

But they are no longer the main question.

Because AI coding tools are not just “coding assistants” anymore.

They are increasingly becoming:

workflow systems

That changes how they should be judged.

───

The old comparison model is breaking

A lot of reviews still treat AI coding tools like static SaaS products.

They ask questions like:

• Which model is smarter?
• Which tool writes cleaner code from a prompt?
• Which editor looks better?
• Which plan is cheaper?

Those are easy questions to ask.

They are also incomplete.

Because real software work is not a one-shot prompt.

It is a loop.

You:

• inspect files
• edit code
• debug things
• change direction
• revisit assumptions
• compare tradeoffs
• keep moving toward something shippable

The tool that wins is often not the one with the flashiest benchmark.

It is the one that makes that loop feel lighter.

───

The real question is workflow compression

The most useful way to compare AI coding tools now is this:

Which tool best compresses the workflow I actually live in?

That means asking:

• Which tool helps me move faster without breaking my flow?
• Which one reduces context switching?
• Which one helps me think more clearly?
• Which one helps me recover when things go wrong?
• Which one gets me closer to a usable result with less drag?

That is a much more serious comparison standard than feature lists.

───

Why the same tool feels great to one person and wrong to another

A developer working inside a real codebase cares about different things than a builder trying to ship an MVP quickly.

A technical founder making architecture decisions cares about different things than someone trying to generate UI fast.

That is why so many debates go nowhere.

People are often comparing tools across different workflows.

And if the workflow is different, the “best” tool changes.

───

This is why Claude and Cursor feel so different

This is also why some comparisons become confusing.

For example:

• Cursor feels stronger when the job is moving through code inside a real coding workflow
• Claude feels stronger when the job is reasoning, debugging, and technical explanation
• Bolt feels stronger when the goal is compressing builder workflow toward product output
• v0 feels stronger when UI generation is the leverage point

These tools do not just compete on quality.

They often compete on which part of the workflow they help most.

That is the real story.

───

The category shift is bigger than people think

The deeper shift happening right now is this:

We are moving from:

• AI as helper

to:

• AI as workflow layer

That means the evaluation standard also has to change.

Instead of asking:

• can it help me?

You increasingly have to ask:

• does it change how I build?
• does it reduce friction across the full loop?
• does it help me ship faster?
• does it actually improve the way I work?

That is the comparison that matters now.

───

Final thought

The best AI coding tool is not necessarily the one with the longest feature list or the most impressive demo.

It is the one that removes the most friction from the path between:

• thought
• implementation
• iteration
• shipping

That is why so many AI coding tool comparisons still feel unsatisfying.

They are comparing features.
But the real battleground is workflow.

───

Full version here:
https://www.codingverdict.com/tools/why-most-ai-coding-tool-comparisons-miss-the-workflow-layer

numpy-ts 1.2.0: Achieving NumPy Parity in TypeScript with float16 Support and Bit-for-Bit RNG Matching

numpy-ts 1.2.0: Achieving NumPy Parity in TypeScript with float16 Support and Bit-for-Bit RNG Matching

The Pain Point: NumPy in JavaScript Land

If you’ve ever tried to port numerical Python code to TypeScript or Node.js, you’ve felt the pain. You’re working with data science code that’s been battle-tested in NumPy, but when you try to reproduce those same computations in JavaScript, you hit a wall of incompatibilities. The random number sequences don’t match. Float16 operations silently fail or produce nonsensical results. You can’t even trust that your RNG will produce identical outputs to Python, making reproducibility—one of the core tenets of scientific computing—impossible.

I experienced this frustration firsthand while building numpy-ts, a TypeScript implementation of NumPy’s core functionality. And I’m happy to say that with version 1.2.0, we’ve closed a major gap: bit-for-bit identical RNG sequences and full float16 support.

But why does this matter so much? Let me explain.

Understanding the Root Cause

Before diving into solutions, let’s understand why achieving NumPy parity is so challenging in JavaScript.

The Random Number Generation Problem

NumPy’s random module uses a sophisticated Philox 4×32 counter-based PRNG (pseudorandom number generator). It’s not just any RNG—it’s designed to be deterministic, reproducible, and statistically sound. The sequence it generates is completely dependent on the initial seed and state.

JavaScript’s native Math.random() is useless for this. It’s:

  1. Non-deterministic across implementations
  2. Non-reproducible without external state management
  3. Single-threaded without parallel stream capability
  4. Not compatible with NumPy’s algorithm

Previous versions of numpy-ts used approximations or different PRNG algorithms entirely, which meant your RNG sequences would diverge from NumPy after the first few values. For scientific computing, this is unacceptable.

The Float16 Gap

Float16 (half-precision floating point) is increasingly important in machine learning and deep learning workflows. Modern GPUs support it natively, and it’s become the standard for model quantization. However, JavaScript has no native float16 support—the language only has Number (float64) and Uint8Array and similar typed arrays.

This meant numpy-ts had to either:

  1. Skip float16 support entirely
  2. Implement a software simulation
  3. Use typed array buffers creatively

The first two options were non-starters for serious numerical work.

Cross-Runtime Compatibility

JavaScript now runs in multiple runtimes: Node.js, Deno, Bun, and browsers. Each has different APIs for I/O, module resolution, and even some WebAPIs. numpy-ts previously required separate entry points for different runtimes, adding friction for users.

The Solution: A Completely Rewritten Approach

Let me walk you through how we solved each of these problems in v1.2.0.

Implementing Bit-for-Bit RNG Matching

The key insight was to implement NumPy’s Philox 4×32 algorithm exactly, down to the bit level. Here’s the core implementation:

// Philox 4x32 counter-based PRNG - bit-for-bit compatible with NumPy
class Philox4x32 {
  private counter: Uint32Array; // 4 x 32-bit counter
  private key: Uint32Array;     // 2 x 32-bit key
  private ctr: number;          // Current position in output buffer

  constructor(seed: number) {
    this.counter = new Uint32Array(4);
    this.key = new Uint32Array(2);
    this.ctr = 4;
    this.seed(seed);
  }

  private seed(seed: number): void {
    // Initialize key from seed using NumPy's exact algorithm
    const seeder = new Philox4x32(0);
    this.key[0] = seed >>> 0;
    this.key[1] = (seed / 4294967296) >>> 0;
  }

  private philoxBumped(): void {
    // Philox permutation function - this MUST match NumPy exactly
    let ctr0 = this.counter[0];
    let ctr1 = this.counter[1];
    let ctr2 = this.counter[2];
    let ctr3 = this.counter[3];

    // 10 rounds of Philox permutation
    for (let i = 0; i < 10; i++) {
      const a = Math.imul(0xd2511f53, ctr0);
      const c = Math.imul(0xcd9e8d57, ctr2);

      ctr0 = (ctr2 ^ (a >>> 0)) >>> 0;
      ctr1 = (ctr3 ^ (c >>> 0)) >>> 0;
      ctr2 = (ctr0 ^ (a >>> 0)) >>> 0;
      ctr3 = (ctr1 ^ (c >>> 0)) >>> 0;

      // Update key (simplified for brevity)
      this.key[0] = (this.key[0] + 0x9e3779b9) >>> 0;
    }

    this.counter[0] = ctr0;
    this.counter[1] = ctr1;
    this.counter[2] = ctr2;
    this.counter[3] = ctr3;
  }

  nextUint32(): number {
    if (this.ctr >= 4) {
      this.philoxBumped();
      this.ctr = 0;
    }
    return this.counter[this.ctr++];
  }

  nextFloat64(): number {
    // Convert two uint32 to float64 using NumPy's exact method
    const a = this.nextUint32();
    const b = this.nextUint32();
    return ((a >>> 5) * 67108864.0 + (b >>> 6)) / 9007199254740992.0;
  }
}

The critical part here is the philoxBumped() method—it implements NumPy’s exact Philox permutation. This isn’t approximate; it’s bit-for-bit identical. No approximations, no rounding differences.

Once you can generate uint32 values identically, converting to floats is just a matter of following NumPy’s bit-shifting and division conventions.

Float16 Implementation

For float16, we use a clever TypedArray approach combined with bit manipulation:

// Float16 stored as Uint16Array, with conversion utilities
export class Float16Array extends Uint16Array {
  constructor(length: number) {
    super(length);
  }

  // Convert float16 bits to float32 for computation
  static toFloat32(float16bits: number): number {
    const sign = (float16bits & 0x8000) >> 15;
    const exp = (float16bits & 0x7c00) >> 10;
    const frac = float16bits & 0x03ff;

    if (exp === 31) {
      return sign ? -Infinity : Infinity;
    }
    if (exp === 0) {
      // Subnormal numbers
      return (sign ? -1 : 1) * (frac / 1024) * Math.pow(2, -14);
    }

    return (sign ? -1 : 1) * (1 + frac / 1024) * Math.pow(2, exp - 15);
  }

  // Convert float32 to float16 bits
  static toFloat16(float32: number): number {
    const view = new Float32Array([float32]);
    const bits = new Uint32Array(view.buffer)[0];

    const sign = (bits >> 31) & 1;
    const exp = ((bits >> 23) & 0xff) - 127 + 15;
    const frac = (bits >> 13) & 0x3ff;

    if (exp >= 31) {
      return (sign << 15) | 0x7c00; // Infinity or NaN
    }
    if (exp <= 0) {
      return (sign << 15); // Zero or subnormal
    }

    return (sign << 15) | (exp << 10) | frac;
  }
}

This approach gives us actual float16 storage efficiency while maintaining compatibility with JavaScript’s float32/64 operations. You get the memory benefits of float16 for large arrays, but can still perform computations using standard IEEE 754 math.

Unified Cross-Runtime Support

Instead of separate entrypoints, we use dynamic runtime detection:

// Auto-detect runtime environment
const getRuntime = (): 'node' | 'deno' | 'bun' | 'browser' => {
  if (typeof globalThis !== 'undefined') {
    if (globalThis.Deno) return 'deno';
    if (globalThis.Bun) return 'bun';
  }
  if (typeof process !== 'undefined' && process.versions?.node) {
    return 'node';
  }
  return 'browser';
};

// Use appropriate I/O based on runtime
const readFileSync = (path: string): string => {
  const runtime = getRuntime();

  switch (runtime) {
    case 'node':
      return require('fs').readFileSync(path, 'utf-8');
    case 'deno':
      return Deno.readTextFileSync(path);
    case 'bun':
      return Bun.file(path).text();
    case 'browser':
      throw new Error('File I/O not supported in browser');
  }
};

This single detection function replaces the need for multiple entry points. Users import from the same location regardless of runtime.

Common Pitfalls and Edge Cases

When implementing float16 and PRNG matching, here are the gotchas I encountered:

1. Uint32 Overflow in JavaScript

JavaScript’s bitwise operators work on 32-bit integers, but numbers are actually 64-bit floats. This causes overflow issues:

// WRONG: loses precision
const a = 0xffffffff + 1; // Becomes 4294967296, loses bit pattern

// RIGHT: use unsigned right shift to maintain 32-bit semantics
const a = (0xffffffff + 1) >>> 0; // Becomes 0

Every arithmetic operation in the Philox implementation must use >>> 0 to maintain proper 32-bit wrapping.

2. Subnormal Float16 Values

Float16’s exponent range is smaller than float32. Subnormal numbers (those near zero) require special handling:

// This value is normal in float32 but subnormal in float16
const value = 1e-5;
const f16bits = Float16Array.toFloat16(value);
const recovered = Float16Array.toFloat32(f16bits);
// recovered !== value due to precision loss

Always validate that your float16 conversions maintain acceptable precision bounds for your application.

3. RNG State Serialization

If users want to save and restore RNG state (essential for reproducibility), you need to handle the entire state:

// Incomplete: only saves seed, not current counter state
const saveState = () => this.seed;

// Complete: saves everything
const saveState = () => ({
  counter: Array.from(this.counter),
  key: Array.from(this.key),
  ctr: this.ctr
});

const loadState = (state: { counter: number[], key: number[], ctr: number }) => {
  this.counter = new Uint32Array(state.counter);
  this.key = new Uint32Array(state.key);
  this.ctr = state.ctr;
};

Performance Improvements

Beyond correctness, v1.2.0 brings significant performance wins. The new RNG implementation is 6x faster than the previous version, primarily because we:

  1. Eliminated array allocations in the hot path
  2. Used TypedArrays exclusively
  3. Removed unnecessary function calls
  4. Leveraged `Math.imul

Want This Automated for Your Business?

I build custom AI bots, automation pipelines, and trading systems that run 24/7 and generate revenue on autopilot.

Hire me on Fiverr — AI bots, web scrapers, data pipelines, and automation built to your spec.

Browse my templates on Gumroad — ready-to-deploy bot templates, automation scripts, and AI toolkits.

Recommended Resources

If you want to go deeper on the topics covered in this article:

Some links above are affiliate links — they help support this content at no extra cost to you.

🌳 Building `ctree`: A Lightweight Unix `tree` Clone in C

When I started learning low-level programming, I wanted to build something practical — not just theory. So I decided to recreate a simplified version of the Unix tree command using C.

This project, called ctree, helped me deeply understand filesystem traversal, recursion, and how real CLI tools work under the hood.

🔗 Source Code

👉 Check out the full project on GitHub:

🚀 What is ctree?

ctree is a lightweight command-line tool that prints directory structures in a tree-like format — just like the Unix tree utility.

But instead of using an existing tool, I built it from scratch using pure C and POSIX APIs.

🧠 What I Learned

This project was more than just printing directories. It taught me:

  • How to use opendir() and readdir() to read directories
  • How to use stat() to detect files vs directories
  • Recursive directory traversal
  • Memory management with strdup() and free()
  • Sorting using qsort()
  • Building CLI tools with argument parsing
  • Designing modular C projects (header + source + main)

✨ Features

Here’s what ctree can do:

📂 Tree Structure

Prints directories and files in a clean tree format:

.
|-- main.c
|-- src
|   L__ utils.c
L__ include

🔢 Count Summary

Shows total directories and files:

3 directories, 2 files

📁 -d (Directories Only)

./ctree -d

➡️ Shows only directories (no files)

📏 -L (Depth Limit)

./ctree -L 2

➡️ Limits how deep the recursion goes

🔤 Sorted Output

Entries are sorted alphabetically using qsort() for clean output.

📦 File Sizes

Files display their size:

main.c (1200 bytes)

🏗 Project Structure

ctree/
├── Tree.h
├── Tree.c
└── main.c
  • Tree.h → declarations and configuration
  • Tree.c → core logic (recursion, sorting, filtering)
  • main.c → CLI argument parsing

⚙️ How It Works

The core logic follows a simple but powerful pipeline:

  1. Read directory entries using readdir()
  2. Store them in an array
  3. Sort them using qsort()
  4. Filter based on flags (-d)
  5. Print with proper indentation
  6. Recursively process subdirectories

This pattern is actually similar to how real CLI tools are implemented.

🔧 Compilation & Usage

Compile:

gcc Tree.c main.c -o ctree

Run:

./ctree

Examples:

./ctree -d
./ctree -L 2
./ctree -d -L 2
./ctree /some/path

⚠️ Challenges I Faced

One tricky bug I encountered was incorrect tree formatting when using the -d flag.

Because I filtered files after counting entries, the program misidentified the “last” element in a directory.

✅ Solution:

I fixed it by:

  • First collecting entries
  • Then filtering them
  • Then printing with correct indexing

This small change made the output accurate and robust.

📈 What’s Next?

I’m planning to improve ctree with:

  • 🎨 Colored output (like real tree)
  • 👁 Support for hidden files (-a)
  • 📊 Human-readable file sizes (KB, MB)
  • ⚡ Performance optimizations

💬 Final Thoughts

Building ctree was a huge step for me in understanding how system-level tools work.

It’s easy to use commands like tree every day — but building one yourself changes how you see the system.

If you’re learning C, I highly recommend building small CLI tools like this. You’ll learn way more than from tutorials alone.

7 Mac Apps I Recommend to Every Developer I Mentor in 2026

I mentor a handful of developers every year — some junior, some mid-level switching to Mac for the first time, some seasoned engineers who just never explored beyond VS Code and Chrome.

The first thing I always do is send them a list of apps. Not a hundred-item dump — just the handful that genuinely changed how I work. These are the seven I keep coming back to in 2026.

1. Raycast — The Launcher That Replaces Five Apps

Raycast

If you’re still using Spotlight, you’re leaving speed on the table. Raycast is a launcher, clipboard manager, window manager, snippet expander, and AI chat interface rolled into one. I’ve watched mentees shave minutes off every hour just by learning the keyboard shortcuts. The free tier covers everything most developers need.

2. Warp — A Terminal Built for Humans

Warp

I switched from iTerm2 to Warp about a year ago and haven’t looked back. It treats terminal output as blocks you can select, copy, and search through individually. The built-in AI command search is perfect for when you can’t remember that obscure find flag. Especially great for developers who are still getting comfortable with the terminal.

3. CleanShot X — Screenshots That Don’t Embarrass You

CleanShot X

Every developer needs to share screenshots — in PRs, in Slack, in docs. CleanShot X gives you annotation, scrolling capture, screen recording, and a floating pin feature that’s perfect for referencing designs while you code. The built-in cloud upload means you get a shareable link instantly. Worth every penny of the one-time purchase.

4. TokenBar — Know What Your AI Tools Actually Cost

TokenBar

This is one I started recommending recently. If you’re using Cursor, Copilot, Claude, or any LLM API, TokenBar sits in your menu bar and tracks token usage across providers in real time. It’s $5 lifetime, and it answers the question every developer using AI tools eventually asks: “Wait, how much am I actually spending?” Especially valuable when mentees start experimenting with API-based workflows and have no sense of what costs what.

5. Obsidian — Your Second Brain, Locally

Obsidian

I tell every mentee to start a knowledge base on day one. Obsidian stores everything as local Markdown files — no lock-in, no cloud dependency. The graph view helps you see connections between notes that you didn’t plan, and the plugin ecosystem is massive. I use it for meeting notes, architecture decisions, and daily logs. Free for personal use.

6. Monk Mode — Block the Feed, Not the App

Monk Mode

This one is specific but powerful. Monk Mode doesn’t block entire websites — it blocks the feed inside them. So you can still use YouTube to search for a tutorial, but the homepage recommendations are gone. Same for Twitter, Reddit, LinkedIn. I’ve seen it help developers who struggle with “just a quick scroll” turning into 45 minutes of lost focus. $15 lifetime, and it works at the native level on macOS.

7. Rectangle — Window Management That Just Works

Rectangle

Free and open source. Drag windows to screen edges or use keyboard shortcuts to snap them into halves, thirds, or quarters. It’s one of those tools that’s so simple you forget it’s not built into macOS. I recommend it to literally everyone who touches a Mac for development work.

Honorable Mentions

  • Homebrew — If you’re a developer on Mac and don’t have Homebrew installed, stop reading and go install it.
  • MetricSync — Not a dev tool, but I recommend it to mentees who want to track nutrition without the tedium. Snap a photo of your meal and AI handles the macros. $5/month on iPhone.
  • Alfred — The OG launcher. Still great if you prefer its Powerpack workflow system over Raycast.
  • Fantastical — Natural language calendar input is a game changer for scheduling-heavy weeks.

Why These Seven?

Every tool on this list solves a specific, daily friction. None of them require you to change your workflow dramatically — they just make the existing one smoother.

The best part: most of these are either free or one-time purchases. No subscription fatigue.

If you mentor developers (or just set up Macs for a living), steal this list. And if you’ve got a recommendation I’m missing, drop it in the comments — I’m always updating my go-to list.

What’s the first app you install on a fresh Mac? I’m curious.

De documentos científicos a la Web 3.0: Historia del HTML

¿Sabías que el primer servidor web era una computadora NeXT en un laboratorio de Suiza? HTML no nació para crear redes sociales ni tiendas online; nació para que los científicos compartieran sus tesis sin volverse locos con el formato.

La línea del tiempo del esqueleto digital

1991 – El Big Bang (HTML 1.0): Tim Berners-Lee publica la primera versión con apenas 18 etiquetas. No había imágenes, ni formularios, solo texto azul y enlaces que funcionaban.

1995 – La era de las tablas (HTML 2.0): Se estandarizan los formularios y las tablas. Los diseñadores empezaron a usar tablas para “maquetar”, algo que hoy nos daría pesadillas.

1997 – El caos de los navegadores (HTML 3.2): Netscape e Internet Explorer empezaron a inventar sus propias etiquetas (como el infame ). W3C puso orden para que la web fuera una sola.

1999 – La madurez (HTML 4.01): Se separó por primera vez el contenido (HTML) de la presentación (CSS). Fue el estándar de oro por más de una década.

2014 – La Revolución (HTML5): Tras años de peleas con Adobe Flash, HTML5 llega para dominar el video, el audio y las aplicaciones móviles de forma nativa.

El “Dato” para Seniors:
¿Sabías que existió HTML 5.1 y 5.2? Pero en 2019, la W3C y el WHATWG (el grupo que realmente hace el trabajo) acordaron que HTML ya no tendría versiones fijas. Ahora es un “Living Standard”. Se actualiza constantemente sin esperar años para una nueva versión.

¿Por qué importa esto hoy?
Entender de dónde venimos nos ayuda a no cometer errores del pasado (como usar etiquetas de formato visual en lugar de semánticas). HTML hoy es una plataforma de software, no solo un documento.

<a href="investigacion.html">Ver reporte científico</a>

<nav>
  <ul>
    <li><a href="/blog">Blog Histórico</a></li>
  </ul>
</nav>

¿Qué es HTML realmente? (No es programación)

Si alguna vez has dicho que “programas en HTML” frente a un desarrollador senior, probablemente viste una pequeña mueca en su rostro. No te preocupes, hoy vamos a definir qué es este lenguaje y por qué es la base de todo lo que ves en internet.

La Anatomía de la Web
HTML no es un lenguaje de programación porque no maneja lógica, bucles o condicionales. Es un Lenguaje de Marcado de Hipertexto (HyperText Markup Language).

Piensa en una página web como un edificio:

HTML es el esqueleto (vigas, ladrillos y estructura).

CSS es la estética (pintura, acabados y decoración).

JavaScript es el sistema funcional (elevadores, luces y sensores).

¿Cómo funciona el “Marcado”?
El navegador es un intérprete. Cuando le envías un archivo .html, él lee etiquetas como

,

o y las traduce en elementos visuales.

El secreto de un Arquitecto Web no es solo poner etiquetas, sino usar las correctas. No es lo mismo un

con texto grande que un

. El segundo le dice a Google y a las tecnologías de asistencia: “¡Oye, esto es lo más importante de esta página!”.

Código Atómico: Tu primera etiqueta

<h1>Hola Mundo, soy John</h1>

<p>Estoy dominando los 2000 temas de HTML en <a href="https://dev.to">Dev.to</a>.</p>

HTML es el lenguaje de la Semántica. Un HTML bien escrito es legible para humanos, accesible para personas con discapacidad y amigable para los robots de búsqueda (SEO). Si el HTML está mal, el resto de la web sufrirá, sin importar cuánto CSS le pongas.

How I Built an SEC Insider Trading Alert System With 3 API Calls

How I Built an SEC Insider Trading Alert System With 3 API Calls

Every public company insider — CEOs, CFOs, board members — is legally required to report stock trades to the SEC within two business days via Form 4 filings. That’s 200+ filings per day, all public data, and it’s one of the most reliable trading signals available.

I built an API that polls EDGAR, parses the XML, and returns clean JSON. Here’s how to use it to build a real-time insider trading alert system.

Why Insider Trades Matter

Academic research consistently shows that insider purchases outperform the market by 7-10% annually. When a CEO buys $2M of their own stock on the open market, they’re putting personal money where their mouth is. That’s a signal worth tracking.

The problem: SEC EDGAR returns raw XML/SGML documents designed for regulators, not developers. Parsing Form 4 filings means dealing with accession numbers, CIK lookups, and XML namespaces.

The API handles all of that and returns structured JSON.

Quick Start: Get Recent Insider Trades

The simplest call — fetch the latest insider trades across all companies:

import requests

API_URL = "https://sec-edgar-insider-alerts-production.up.railway.app"

# Get recent insider trades (last 3 days)
response = requests.get(f"{API_URL}/sec/insider-trades", params={
    "days": 3,
    "limit": 20
})

data = response.json()
print(f"Found {data['total']} filings")

for filing in data["filings"]:
    print(f"{filing['filedDate']} | {filing['insider']['name']} | {filing['company']['name']}")

Output:

Found 847 filings
2026-03-27 | Tim Cook | Apple Inc.
2026-03-27 | Jensen Huang | NVIDIA Corp
2026-03-26 | Satya Nadella | Microsoft Corporation
...

Filter by Company Ticker

Want to monitor a specific stock? Use the company endpoint:

# Get all AAPL insider trades in the last 30 days
aapl = requests.get(f"{API_URL}/sec/insider-trades/company/AAPL", params={
    "days": 30,
    "limit": 50
})

data = aapl.json()
print(f"AAPL insider trades: {data['total']}")

for filing in data["filings"]:
    insider = filing["insider"]["name"]
    date = filing["filedDate"]
    print(f"  {date} — {insider}")

This covers 10,400+ ticker symbols — basically every public company on EDGAR.

Get Full Transaction Details

The filing detail endpoint is where it gets interesting. Each Form 4 contains the actual transaction data — shares bought/sold, price per share, and post-transaction holdings:

# Get full Form 4 details
detail = requests.get(
    f"{API_URL}/sec/insider-trades/filing/0002114927-26-000005"
)

filing = detail.json()
print(f"Insider: {filing['owner']['name']}")
print(f"Company: {filing['issuer']['name']} ({filing['issuer']['ticker']})")
print(f"Role: Director={filing['owner']['isDirector']}, Officer={filing['owner']['isOfficer']}")

for tx in filing["transactions"]:
    action = tx["codeLabel"]  # "Purchase", "Sale", "Option Exercise"
    shares = tx["shares"]
    price = tx.get("pricePerShare", "N/A")
    print(f"  {action}: {shares} shares @ ${price}")

Response includes:

  • Transaction codes: P (Purchase), S (Sale), M (Option Exercise), G (Gift), A (Grant)
  • Insider roles: Director, Officer (with title), 10% Owner
  • Both non-derivative and derivative transactions (options, warrants)
  • Post-transaction holdings so you can see their total position

Build a Simple Alert Bot

Here’s a practical example — a script that checks for large insider purchases and sends alerts:

import requests

API_URL = "https://sec-edgar-insider-alerts-production.up.railway.app"
WATCHLIST = ["AAPL", "MSFT", "GOOGL", "NVDA", "TSLA", "META", "AMZN"]
MIN_VALUE = 100000  # Alert on purchases over $100K

def check_insider_buys():
    alerts = []

    for ticker in WATCHLIST:
        resp = requests.get(f"{API_URL}/sec/insider-trades/company/{ticker}", params={
            "days": 1,
            "limit": 20
        })
        data = resp.json()

        for filing in data.get("filings", []):
            # Get full details to check transaction value
            detail = requests.get(f"{API_URL}{filing['detailUrl']}")
            info = detail.json()

            for tx in info.get("transactions", []):
                if tx["code"] == "P":  # Open market purchase
                    value = (tx.get("shares", 0) or 0) * (tx.get("pricePerShare", 0) or 0)
                    if value >= MIN_VALUE:
                        alerts.append({
                            "ticker": ticker,
                            "insider": info["owner"]["name"],
                            "shares": tx["shares"],
                            "value": value,
                            "date": tx["date"]
                        })

    return alerts

# Run it
buys = check_insider_buys()
for buy in buys:
    print(f"🚨 {buy['ticker']}: {buy['insider']} bought {buy['shares']} shares (${buy['value']:,.0f})")

JavaScript Version (Node.js)

const API_URL = "https://sec-edgar-insider-alerts-production.up.railway.app";

// Fetch recent insider trades
async function getInsiderTrades(ticker, days = 7) {
  const res = await fetch(
    `${API_URL}/sec/insider-trades/company/${ticker}?days=${days}&limit=50`
  );
  const data = await res.json();

  return data.filings.map(f => ({
    date: f.filedDate,
    insider: f.insider.name,
    company: f.company.name,
    detailUrl: f.detailUrl
  }));
}

// Get transaction details
async function getFilingDetail(accession) {
  const res = await fetch(`${API_URL}/sec/insider-trades/filing/${accession}`);
  return res.json();
}

// Example: Find all purchases in NVDA this month
const trades = await getInsiderTrades("NVDA", 30);
console.log(`Found ${trades.length} NVDA insider filings`);

for (const trade of trades.slice(0, 5)) {
  console.log(`${trade.date} — ${trade.insider}`);
}

Use Cases

  • Trading signals: Cluster analysis on insider buys (3+ insiders buying within a week is historically bullish)
  • Compliance dashboards: Monitor executive trading windows and blackout periods
  • Financial research: Backtest insider sentiment vs. stock performance
  • Slack/Discord bots: Push alerts when specific insiders trade
  • Portfolio screening: Filter your holdings by recent insider activity

Data Source & Reliability

The API pulls directly from SEC EDGAR’s EFTS API and ATOM feed — official government data, not scraped from third-party sites. Polling happens every 5 minutes during market hours, with fallback to the ATOM feed if EFTS is down.

Covers all Form 4 filers across NYSE, NASDAQ, and OTC markets. No rate limiting on the direct endpoint.

Try It

The API is live and free to use:

No API key needed for the direct Railway endpoint. Just start making requests.

“Why I Stopped Trusting Backtests and Built a Live Paper Trading Tournament Instead”

Backtesting feels productive. You write a strategy, run it against 5 years of historical data, watch the equity curve trend up and to the right, and start daydreaming about returns.

Then you paper trade it. And it falls apart within the first two weeks.

I’ve been through this cycle enough times to know that backtesting and live trading are fundamentally different problems. Backtesting is optimization. Live trading is survival.

What Backtesting Actually Tests

Backtesting tests whether your strategy would have worked on data that you implicitly shaped your strategy around. Even with walk-forward validation and out-of-sample testing, there’s survivor bias baked in — you chose the instruments, the time period, the features. The strategy is always somewhat aware of the future it’s supposedly predicting.

The four biases I kept running into (and eventually fixed in TradeSight):

  1. Look-ahead bias — using daily close prices as if they’re available intraday
  2. Survivorship bias — only testing on stocks that still exist
  3. Overfitting — RSI + MACD + Bollinger + custom filter = it works on exactly this data
  4. Slippage blindness — assuming fills at the exact signal price

These aren’t edge cases. They’re the default state of most backtesting setups.

The Tournament Idea

The shift I made: instead of running one strategy and judging it on historical performance, I run multiple strategies simultaneously against live paper trading data and let them compete.

The setup is simple. Each strategy gets the same starting capital. They all receive the same market data in real-time via Alpaca’s paper trading API. At the end of a session (I run overnight — markets are less noisy for my RSI-based signals), I score each strategy on:

  • Net return
  • Max drawdown
  • Win rate
  • Sharpe ratio
  • Number of trades (penalizes churning)

The top performers roll forward. The losers get replaced or adjusted. It’s evolutionary selection on a small scale.

Why This Works Better

A few things surprised me when I switched to this approach:

Failure is fast. A strategy that’s broken in live conditions fails within days, not months. With backtesting, I’d build elaborate validation pipelines to catch flaws. With tournaments, the market does it for you.

Signal quality becomes obvious. When a strategy is profitable in backtesting but losing in live trading, it’s almost always overfitting or look-ahead bias. The gap between backtest and live performance is diagnostic data.

You stop over-engineering. When you’re running 4-6 strategies at once with real-time data, simple beats complex almost every time. RSI crossover with clean entry/exit rules usually outperforms my “improved” version with 8 extra filters.

The Implementation

The core of TradeSight is a signal loop that runs every 30 minutes during market hours and overnight for swing setups:

for strategy in active_strategies:
    signals = strategy.generate_signals(market_data)
    for signal in signals:
        if passes_risk_guard(signal, portfolio_state):
            execute_paper_trade(signal)

    strategy.score = calculate_score(strategy.trade_history)

# Tournament: keep top performers
ranked = sorted(active_strategies, key=lambda s: s.score, reverse=True)
active_strategies = ranked[:keep_top_n]

The risk guard is important — it enforces position sizing limits and a correlation guard that prevents the portfolio from doubling up on correlated instruments (which backtests almost never catch).

What I’ve Learned From Running Tournaments

After running this for a few months:

  • RSI with sector-adjusted thresholds consistently outperforms vanilla RSI
  • Mean reversion beats momentum on overnight holding periods for the instruments I trade
  • Drawdown limits matter more than return targets — strategies that survive drawdowns compound better over time
  • The correlation guard saves you regularly — tech sector moves together and naive strategies pile into correlated positions

The most useful output isn’t “this strategy made 3.2% this week.” It’s “this strategy’s drawdown profile looks unstable — here’s why.”

The Actual Code

TradeSight is open source: github.com/rmbell09-lang/tradesight

It runs on Python 3.9+, connects to Alpaca for paper trading data, and stores tournament results in SQLite. Setup takes about 10 minutes if you have an Alpaca paper account (free to create).

If you’re tired of backtesting giving you false confidence, this approach is worth trying. It won’t make bad strategies good — but it’ll tell you which ones are actually bad a lot faster.

Built a similar system? Running paper trading tournaments against something other than RSI? I’d like to hear what’s working.

How I Built a Memory-Safe Steganography Engine in Rust to Protect Data from AI Scrapers

How I Built a Memory-Safe Steganography Engine in Rust to Protect Data from AI Scrapers

As AI models scale, data provenance is becoming a massive engineering challenge. Automated web scrapers are vacuuming up datasets without any regard for creator licenses or intellectual property.

I wanted to build a mathematical solution to this problem, so I architected Sigil: a zero-knowledge cryptographic vault that embeds verifiable, HMAC-SHA256 signed ownership IDs directly into the pixels of an image.

While the desktop vault (built with Tauri, Svelte, and an offline SQLite daemon) is strictly closed-source to protect the cryptographic keys, I realized that “Security by Obscurity” isn’t enough. If AI companies don’t know how to read the hidden IDs, they will just scrape the images anyway.

So, I open-sourced the extraction layer. Here is a deep dive into how I used Rust to build a memory-safe Least Significant Bit (LSB) steganography reader.

The Concept: LSB Steganography

Every pixel in a standard image is made of Red, Green, and Blue channels. Each channel is represented by a byte (8 bits), with values ranging from 0 to 255.

If you change the Least Significant Bit (the absolute last 1 or 0 in that byte), the color change is invisible to the human eye. But mathematically, you can use those hidden bits to store a secret payload—like a 32-byte cryptographic ID.

The Rust Implementation

To make this blazing fast and completely safe from memory leaks, I used Rust’s image crate to parse the pixels. Here is the exact open-source reference implementation for extracting the payload:

use image::GenericImageView;

/// Extracts a hidden Sigil Cryptographic ID from an image's LSB layer.
pub fn verify_steganography(path: &str, expected_id_len: usize) -> Result<String, String> {
    let img = image::open(path).map_err(|e| e.to_string())?.to_rgba8();
    let mut bits = Vec::with_capacity(expected_id_len * 8);

    // 1. Extract the Least Significant Bits
    for pixel in img.pixels() {
        for channel in 0..3 { // Iterate over R, G, B
            if bits.len() < expected_id_len * 8 {
                // The bitwise AND operator isolates the final bit
                bits.push(pixel[channel] & 1);
            }
        }
    }

    // 2. Reconstruct the bytes
    let mut extracted_bytes = Vec::new();
    for chunk in bits.chunks(8) {
        if chunk.len() == 8 {
            let mut byte = 0u8;
            for (i, &bit) in chunk.iter().enumerate() {
                // Shift the bit back into its correct position
                byte |= bit << (7 - i);
            }
            extracted_bytes.push(byte);
        }
    }

    // 3. Return the Hex String
    Ok(hex::encode(extracted_bytes))
}

Breaking Down the Bitwise Math

The magic happens in two specific lines of code:

  • pixel[channel] & 1: This is a bitwise AND operation. By comparing the pixel’s byte against 00000001, we wipe out the first 7 bits and isolate only the final bit. If the pixel is even, it returns 0. If it is odd, it returns 1. We push this bit into our Vector.
  • byte |= bit << (7 - i): Once we have 8 hidden bits, we need to stitch them back into a single byte. We use the left-shift operator (<<) to push the bit into the correct slot (from position 7 down to 0), and the bitwise OR operator (|=) to combine them into the final u8.

The Open Standard

By publishing this extraction logic, AI procurement teams can now integrate this exact function into their scraping pipelines. If their crawler detects a payload, they know the asset is cryptographically locked and requires an API clearance.

You can check out the full open-source extraction standard on GitHub, or visit the lightning-fast Astro-powered documentation site here:
https://nishal21.github.io/Sigil-extractor/

I’d love to hear your thoughts on the Rust implementation or LSB steganography in general!

https://github.com/nishal21/Sigil-extractor

Docling CLI to parse PDFs and export it to multiple formats

What is Docling ???

Docling is an open source document processing library that converts various document formats into structured outputs.
Docling plays an important part in the RAG pipeline.

I’ll be taking you through the process of parsing PDFs into structured formats.

Step 1: Set up

  • Create the project structure in your terminal;
mkdir docling_cli
cd docling_cli
  • Create your virtual environment and activate it.
    Fedora


Windows

Step 2: Installing docling

pip install docling
docling --version

Fedora

Windows

Check the docling’s version

Step 3: Creating input and outputs folders

  • create a folder called data where you will stored your desired pdfs.
  • create a new folder and name it outputs
    then inside the folders create new folders called; markdown outputs, html outputs and json outputs.

Step 4: Changing the pdfs into html format

docling --to html *.pdf --output ~Documents/docling_cli/outputs/html_outputs

Step 5: Changing the pdfs into other formats

1. Markdown

2. Json

3. Plain text

4. yaml

5. html_split_page

6. DOCtags

7. vtt

Step 6: Analyzing the result findings.

I used three types of pdfss;
one with tables, the other with text and images and the other had tables and paragraphs. Here are my key findings;

1. Pdf with tables

  • In HTML, the rows and columns came out better than they were in the original pdf.
  • Markdown outputs were good too as it wrote the tables in markdown format without losing anything.
  • JSON was broke everything down into nested objects
  • Plain text was good too but not as compared to markdown.

2. Pdf with text and images

  • HTML lost the color of the images.

3. Pdf with tables and paragraphs

  • Paragraphs in all formats came out nicely as texts.