<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Gugubibi</title>
    <description>The latest articles on DEV Community by Gugubibi (@gugubibi).</description>
    <link>https://dev.to/gugubibi</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%2F3885340%2F8cd2f97f-12d9-43c1-ace7-84a4532d823b.png</url>
      <title>DEV Community: Gugubibi</title>
      <link>https://dev.to/gugubibi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gugubibi"/>
    <language>en</language>
    <item>
      <title>What 19 GB of Memory Compression Taught Me About MLX on M1 Max</title>
      <dc:creator>Gugubibi</dc:creator>
      <pubDate>Mon, 20 Apr 2026 09:29:24 +0000</pubDate>
      <link>https://dev.to/gugubibi/what-19-gb-of-memory-compression-taught-me-about-mlx-on-m1-max-3eha</link>
      <guid>https://dev.to/gugubibi/what-19-gb-of-memory-compression-taught-me-about-mlx-on-m1-max-3eha</guid>
      <description>&lt;h1&gt;
  
  
  What 19 GB of Memory Compression Taught Me About MLX on M1 Max
&lt;/h1&gt;

&lt;h2&gt;
  
  
  The moment something was wrong
&lt;/h2&gt;

&lt;p&gt;I opened Activity Monitor on my M1 Max one afternoon and saw this: Memory Used 60.74 GB out of 64, compressed memory 19.69 GB, swap starting to fill. The SwiftUI dashboard I use to drive my multi-agent quant stack had hung. Python — the backend process holding an MLX-loaded Qwen 3.6 35B-A3B model — reported 44 GB in Activity Monitor's "Memory" column.&lt;/p&gt;

&lt;p&gt;My first thought was the obvious one: memory leak. Shut it down, restart, move on.&lt;/p&gt;

&lt;p&gt;That would have been wrong. What I found instead was a much more interesting problem about how macOS handles Metal unified memory when a large model sits idle between inferences — and the fix turned out to be a single MLX API call I had never used.&lt;/p&gt;

&lt;p&gt;This is the honest write-up: what broke, what I measured, what the fix actually was, and what I'm still not sure about.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I was actually running
&lt;/h2&gt;

&lt;p&gt;One M1 Max, 64 GB unified memory. One Python process holding the MLX framework with a Q8-quantized 35B-A3B MoE model loaded. About 35 GB of that goes to model weights in Metal-accessible memory; the rest of the process is the FastAPI backend, twelve specialized agents sharing the single model through a priority queue, a SQLite paper-trading book, and assorted content-generation loops.&lt;/p&gt;

&lt;p&gt;Uptime at the point of the snapshot: just under 8 hours since the last backend restart.&lt;/p&gt;

&lt;p&gt;In normal operation, Activity Monitor should show something like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Python process: ~35-40 GB in the "Memory" column&lt;/li&gt;
&lt;li&gt;Wired: 2-3 GB (kernel)&lt;/li&gt;
&lt;li&gt;Compressed: low single digits&lt;/li&gt;
&lt;li&gt;Free + reclaimable inactive: 15-20 GB&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What I saw instead:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Python process: 44 GB&lt;/li&gt;
&lt;li&gt;Compressed: &lt;strong&gt;19.69 GB&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Swap: 1.57 GB and climbing&lt;/li&gt;
&lt;li&gt;Free: 3 GB&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The compressed number was the interesting one. Not the total.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why compressed memory is the signal, not the problem
&lt;/h2&gt;

&lt;p&gt;macOS has an in-kernel memory compressor that tries to keep a working set resident by compressing pages that processes have allocated but aren't actively touching. When compressed memory grows, it usually means somewhere a process has a big chunk of memory that's "cold" — allocated but not referenced often enough to count as active.&lt;/p&gt;

&lt;p&gt;Two-to-one is a rough compression ratio. 19.69 GB compressed suggests maybe 40 GB of "owed" memory being squeezed in.&lt;/p&gt;

&lt;p&gt;On a normal desktop, this is invisible and fine. On a machine running a 35 GB model, it's a red flag: if the model weights are being compressed and decompressed as the compressor swaps them in and out of a resident state, every inference pays a cost to decompress pages before Metal can use them. CPU cycles burn. Latency drifts. Over hours, the machine becomes sluggish in a way that's hard to attribute.&lt;/p&gt;

&lt;p&gt;The question became: why are my model weights going inactive between inferences in the first place?&lt;/p&gt;

&lt;h2&gt;
  
  
  The thing I didn't know about Apple Silicon Metal
&lt;/h2&gt;

&lt;p&gt;On Apple Silicon, CPU and GPU share the same physical RAM. That's the unified memory advantage. But "unified" doesn't mean "all memory is treated the same." Metal exposes a few storage modes, and the one MLX uses by default for model weights is &lt;code&gt;shared&lt;/code&gt; — accessible to both CPU and GPU.&lt;/p&gt;

&lt;p&gt;Here's the thing I had to learn the hard way: &lt;code&gt;shared&lt;/code&gt; storage pages are pageable. They can be marked inactive by the kernel. They can be compressed. From the operating system's perspective, a chunk of Metal-allocated memory that isn't actively being read or written looks exactly like a process's idle heap. It gets the same treatment.&lt;/p&gt;

&lt;p&gt;So the loop I was producing was this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Model loaded into Metal shared storage (~35 GB)&lt;/li&gt;
&lt;li&gt;Inference fires, GPU reads weights, decoder runs&lt;/li&gt;
&lt;li&gt;Inference finishes&lt;/li&gt;
&lt;li&gt;Seconds pass. No one touches the weights.&lt;/li&gt;
&lt;li&gt;Kernel marks pages inactive&lt;/li&gt;
&lt;li&gt;Compressor kicks in, squeezes cold pages&lt;/li&gt;
&lt;li&gt;Next inference arrives&lt;/li&gt;
&lt;li&gt;GPU needs to read weights → decompress first → latency&lt;/li&gt;
&lt;li&gt;Return to 1.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Over hours, the compressor works harder and harder. The machine isn't leaking memory. It's thrashing a 35 GB working set against a compression algorithm that assumes cold data will stay cold. It won't stay cold. It's a running model.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix I should have known about six months ago
&lt;/h2&gt;

&lt;p&gt;MLX has an API called &lt;code&gt;mx.metal.set_wired_limit(bytes)&lt;/code&gt;. It tells Metal: "keep up to N bytes of memory resident and uncompressible." I had never called it. The default is unlimited-but-unpinned, which means nothing is protected.&lt;/p&gt;

&lt;p&gt;I set it to 45 GB — enough to cover the ~35 GB of model weights plus a few GB of KV cache and scratch. Added two more for good measure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;mx.metal.set_cache_limit(512 MB)&lt;/code&gt; — cap the Metal compile cache so it can't drift over time.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;mx.metal.set_memory_limit(48 GB)&lt;/code&gt; — hard ceiling so Metal refuses to allocate beyond that. Fail loudly instead of OOM.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All three calls go in &lt;code&gt;_load_model&lt;/code&gt; before &lt;code&gt;mlx_lm.load()&lt;/code&gt; allocates weights, so Metal knows the budget up front.&lt;/p&gt;

&lt;p&gt;Results (one backend restart later):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Before&lt;/th&gt;
&lt;th&gt;After&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Python "Memory" column&lt;/td&gt;
&lt;td&gt;44 GB&lt;/td&gt;
&lt;td&gt;~40 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compressed&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;19.69 GB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1.7 GB&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Swap&lt;/td&gt;
&lt;td&gt;1.57 GB&lt;/td&gt;
&lt;td&gt;1.6 GB (historical, drains)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Free + reclaimable inactive&lt;/td&gt;
&lt;td&gt;3 GB&lt;/td&gt;
&lt;td&gt;~30 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Compressed memory dropped by 91%. The model wasn't leaking. The kernel just wasn't pinning it, because I had never told it to.&lt;/p&gt;

&lt;h2&gt;
  
  
  Four more layers I added because I don't trust a single fix
&lt;/h2&gt;

&lt;p&gt;Getting to 1.7 GB compressed on a fresh restart is nice. Keeping it there over days of uptime is different. I layered four more defenses in case any of them mattered:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Clear the Metal compile cache after heavy inference.&lt;/strong&gt; My content pipeline runs &lt;code&gt;max_tokens ≥ 500&lt;/code&gt; inferences regularly (sectional generation for long-form writeups). Metal accumulates a compile/scratch cache that doesn't matter for a single run but drifts. Added &lt;code&gt;mx.metal.clear_cache()&lt;/code&gt; as an automatic hook at the end of any inference above that token threshold.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A memory-pressure watchdog.&lt;/strong&gt; A background task polls &lt;code&gt;psutil.virtual_memory()&lt;/code&gt; every five minutes. If Metal cache exceeds 1 GB, clear it automatically. If total system memory used exceeds 60 GB, print a warning. Not an alarm — just a log signal I can grep later.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A nightly restart.&lt;/strong&gt; Every night at 4 AM local time, the backend does &lt;code&gt;os._exit(1)&lt;/code&gt;. LaunchAgent &lt;code&gt;KeepAlive&lt;/code&gt; respawns it in about a minute. Fresh MLX state, fresh Python heap. The warmup cost (~60 seconds of MLX reload) is free because I'm asleep and nothing depends on it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Manual unload / reload API.&lt;/strong&gt; &lt;code&gt;POST /resources/mlx-unload&lt;/code&gt; sets a flag, drops the model reference, calls &lt;code&gt;mx.metal.clear_cache()&lt;/code&gt;. Inference calls after that fail fast with a clear error. &lt;code&gt;POST /resources/mlx-reload&lt;/code&gt; brings the model back in about 60 seconds. This is for when I want the full 40 GB of Metal memory for something else temporarily. Trade scanners and the paper engine keep running because they don't depend on MLX at all — they're pure Python against SQLite.&lt;/p&gt;

&lt;p&gt;All five together survive multiple-day uptime without drift.&lt;/p&gt;

&lt;h2&gt;
  
  
  The parts I'm still not sure about
&lt;/h2&gt;

&lt;p&gt;The 45 GB wired limit is a guess. It works on my machine with this exact model. If I added a second model, or switched to a denser quantization, or loaded more aggressive KV cache — I'd need to re-tune. I don't have a systematic way to pick the number other than "model weights plus headroom, less than the point where the rest of macOS starves."&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;set_memory_limit(48 GB)&lt;/code&gt; hard ceiling may be too aggressive. I haven't stress-tested what happens when the limit is actually hit. Probably Metal throws an OutOfMemoryError and the inference fails with a clear traceback, which is what I want. But I haven't caused it on purpose yet.&lt;/p&gt;

&lt;p&gt;The watchdog threshold — clear cache above 1 GB, warn above 60 GB — is arbitrary. I set those based on vibes and one afternoon of measurement. A more disciplined version would instrument several days of data and pick thresholds from actual distribution percentiles.&lt;/p&gt;

&lt;p&gt;The nightly restart is the scariest one. It assumes nothing important is mid-execution at 4 AM. For now that's true because I'm a solo operator. For a multi-user production stack, it would not be acceptable, and I'd need a graceful-drain + cutover pattern instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd tell past-me six months ago
&lt;/h2&gt;

&lt;p&gt;If you're running a large MLX model on Apple Silicon and you've never touched &lt;code&gt;mx.metal.set_wired_limit&lt;/code&gt;, check Activity Monitor's Compressed Memory number after a few hours of uptime. If it's in double-digit GB, you're probably paying a compression/decompression tax on every inference.&lt;/p&gt;

&lt;p&gt;The fix is three lines:&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;mlx.core&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;mx&lt;/span&gt;
&lt;span class="n"&gt;mx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;metal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_wired_limit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;45&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;     &lt;span class="c1"&gt;# pin the model in resident RAM
&lt;/span&gt;&lt;span class="n"&gt;mx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;metal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_cache_limit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;512&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&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;# cap Metal compile/scratch
&lt;/span&gt;&lt;span class="n"&gt;mx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;metal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_memory_limit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;48&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    &lt;span class="c1"&gt;# fail loud above this, don't OOM
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. Works on M1 and M2 generations. I haven't tested on M3 or M4 Pro / Max, but the API is the same and the underlying Metal behavior should be too.&lt;/p&gt;

&lt;p&gt;The broader lesson I'm taking away: unified memory is a genuine advantage for local-first AI, but it inherits the OS's defaults for normal application memory. A 35 GB working set of neural-network weights is not what macOS's memory manager was designed for. The API to tell it "treat this differently" is there; I just had to know it existed.&lt;/p&gt;

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

&lt;p&gt;I'm packaging the full hygiene layer as a small open-source helper — tentatively &lt;code&gt;mlx-memory-safe&lt;/code&gt; — so anyone running MLX on a Mac can drop it in with one import instead of reading three sections of this post to rediscover the same fixes. Should land on GitHub and PyPI in the next week or two, with a separate write-up of the package internals.&lt;/p&gt;

&lt;p&gt;If you've hit something similar, or if you've tested &lt;code&gt;set_wired_limit&lt;/code&gt; on M3/M4 and seen different behavior, I'd love to hear about it. I still don't have a clean mental model for when &lt;code&gt;shared&lt;/code&gt; storage mode pages leave the wired set under real-world pressure, and that gap is the next thing I want to understand.&lt;/p&gt;

&lt;p&gt;Come along for the ride.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Disclaimer:&lt;/strong&gt; This post reflects one solo operator's configuration on one M1 Max with 64 GB of unified memory in April 2026, running MLX + Qwen 3.6 35B-A3B Q8. Specific numbers (compressed GB, tok/s, wired limit) will differ on other hardware, other models, and other workloads. Test on your own setup before adopting any threshold as a default.&lt;/p&gt;

</description>
      <category>mlx</category>
      <category>apple</category>
      <category>ai</category>
      <category>performance</category>
    </item>
    <item>
      <title>Why Apple Silicon Quietly Won the Local-AI Race (April 2026)</title>
      <dc:creator>Gugubibi</dc:creator>
      <pubDate>Sat, 18 Apr 2026 09:30:37 +0000</pubDate>
      <link>https://dev.to/gugubibi/why-apple-silicon-quietly-won-the-local-ai-race-april-2026-34g7</link>
      <guid>https://dev.to/gugubibi/why-apple-silicon-quietly-won-the-local-ai-race-april-2026-34g7</guid>
      <description>&lt;h1&gt;
  
  
  Why Apple Silicon Quietly Won the Local-AI Race (April 2026)
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Executive summary
&lt;/h2&gt;

&lt;p&gt;While the public AI narrative is dominated by capex wars and cloud GPU shortages, a quieter shift has happened on the desktop. A single Apple Silicon laptop with 64GB of unified memory now runs a 35-billion-parameter mixture-of-experts model at usable speed, with no API key, no rate limit, and no per-token bill. SleepyQuant — a build-in-public AI quant trading project — runs its full 12-agent stack on one M1 Max. Last week we swapped the primary inference model from a 4-bit to an 8-bit quantization. RAM went from about 19GB to about 35GB active. Decode speed dropped from roughly 50 tokens per second to about 10. The post that follows is the honest account of that trade, why it was the right call, and what unified memory architecture actually changes for anyone trying to ship local-first AI in 2026.&lt;/p&gt;

&lt;h2&gt;
  
  
  Thesis
&lt;/h2&gt;

&lt;p&gt;The default assumption of the last two years is that meaningful AI requires meaningful infrastructure: a data center, a GPU cluster, an API contract. Apple's hardware bet quietly inverts that assumption for a specific category of work — single-operator inference of capable open-weight models on commodity hardware.&lt;/p&gt;

&lt;p&gt;The mechanism is unified memory architecture, or UMA. On a traditional desktop, the CPU and GPU each own separate memory pools. To run a large model on the GPU, the model weights must be copied across the PCIe bus, then activations move back and forth for every layer. The cost is latency, energy, and an effective ceiling on model size set by the GPU's dedicated VRAM. On Apple Silicon, CPU, GPU, and Neural Engine cores share one unified memory pool on the same package. There is no copy step. The same 64GB of physical RAM is available to whichever processing unit needs it, in whatever ratio the workload demands.&lt;/p&gt;

&lt;p&gt;This sounds like an engineering footnote. It is not. It is the mechanism that lets a 35B-parameter model fit and run on a $4,000 laptop instead of an $80,000 server. For workloads that are bounded by single-user inference latency and privacy — exactly the workloads small builders, indie developers, and solo operators care about — that changes the economics of building with AI from "raise a seed round for compute" to "buy the laptop."&lt;/p&gt;

&lt;h2&gt;
  
  
  Deep dive: what we actually run
&lt;/h2&gt;

&lt;p&gt;SleepyQuant runs on one M1 Max with 64GB of unified memory. The primary inference engine is the MLX framework — Apple's open-source machine learning library tuned for Apple Silicon. The model is Qwen 3.6 35B-A3B, a sparse mixture-of-experts (MoE) architecture, served at 8-bit quantization. The active model footprint is around 35GB. With Python's process overhead and the rest of the agent stack loaded, total active and wired memory sits between 44GB and 47GB. That leaves a sliver of headroom under the 48GB practical ceiling we set for ourselves before macOS starts swap-paging into the SSD and the user-visible latency falls off a cliff.&lt;/p&gt;

&lt;p&gt;Decode throughput at 8-bit is approximately 10 tokens per second. At 4-bit, the same model decoded at 49–60 tokens per second. The 5x slowdown is real, and it is not free. The reason we accepted it is that 8-bit is meaningfully sharper on data-aware tasks — content evaluation against a fact list, fabrication detection in generated drafts, structured output parsing. For a build-in-public project where every published number should be defensible, "slightly slower but more truthful" is the right trade. For a real-time chat application, it would not be.&lt;/p&gt;

&lt;p&gt;The sparse MoE design adds one more wrinkle. Qwen 3.6 35B-A3B activates only ~3B parameters per token, which is what makes its decode throughput tractable on commodity hardware in the first place. But MoE models degenerate into repetitive word-salad when forced to generate long single completions — anything past about 500 output tokens reliably produces collapsing prose where the same phrases re-circulate. The fix is not "buy a denser model"; the fix is sectional generation. Long content gets split into 250–400-token sections that are generated independently and concatenated. The model never has to hold a 1500-word output in its working window at once. This is a structural workaround for an architectural property of MoE, not a hack.&lt;/p&gt;

&lt;p&gt;On top of that base inference layer, SleepyQuant orchestrates twelve specialized agents — content drafting, quality evaluation, trading scan, risk analysis, news ingestion, and so on — sharing the single MLX runtime through a sequential lock that prevents two simultaneous Metal GPU calls from crashing the device. The lock turns into a priority queue: user-facing chat outranks agent tool calls, which outrank background automation. Twelve agents share one inference engine, not twelve cloud endpoints.&lt;/p&gt;

&lt;p&gt;The full operational footprint: one laptop, one model on disk, one Metal-bound process, no recurring infrastructure cost. The bill of materials is the laptop and the electricity to run it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Counter-argument: when Apple Silicon loses
&lt;/h2&gt;

&lt;p&gt;The story above is selective. Apple Silicon is the wrong tool for several common AI workloads, and pretending otherwise sets up failure.&lt;/p&gt;

&lt;p&gt;Training is the obvious one. Pre-training a foundation model from scratch, or even continued pre-training on a domain-specific corpus, demands cluster-grade compute and high-bandwidth interconnects that consumer hardware does not provide. The unified memory advantage works in the inference direction; in the training direction, dedicated GPU farms remain dominant.&lt;/p&gt;

&lt;p&gt;Multi-tenant serving is the second loss case. A single MLX-bound laptop serves one inference at a time through a lock. That works for a solo operator running an internal stack. It does not work for a SaaS product with concurrent users, where horizontal scaling on cloud GPU is the rational architecture.&lt;/p&gt;

&lt;p&gt;High-throughput batch inference is the third. If the workload is "score 100,000 documents tonight," a multi-GPU server with batched attention will eat the laptop's lunch. The laptop wins on per-token cost for low volume; cloud batch wins on throughput per dollar at scale.&lt;/p&gt;

&lt;p&gt;Continuous fine-tuning is the fourth, and the one most people forget. The Apple Silicon stack excels at running pre-trained models efficiently. It is weaker at adapting them quickly. If the strategy depends on retraining on yesterday's market data every night to stay competitive, single-laptop inference is a structural disadvantage compared to a hedge fund operating its own GPU cluster.&lt;/p&gt;

&lt;p&gt;These limitations are real. They constrain where the local-first thesis applies. They do not invalidate it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verdict
&lt;/h2&gt;

&lt;p&gt;The local-first Apple Silicon stack is the right answer for a specific shape of project: a single operator (or small team), inference-dominant workloads, sensitivity to per-token cost, sensitivity to data leaving the machine, and acceptable latency at the throughput a sequential lock allows. Build-in-public projects, indie research, internal tooling, privacy-sensitive personal automation — all of these fit the shape.&lt;/p&gt;

&lt;p&gt;For training, multi-tenant serving, high-throughput batch, and continuous fine-tuning at production scale, the cloud GPU stack remains the right answer.&lt;/p&gt;

&lt;p&gt;What changed in 2026 is not that Apple Silicon is suddenly competitive everywhere. What changed is that the band of workloads for which a single laptop is sufficient has widened to include things that, two years ago, demanded a serious infrastructure budget. A 35B-parameter MoE running on one M-series chip at 10 tokens per second is not a benchmark to brag about against H100 clusters. It is, however, a baseline good enough to run a real product, on a real budget, with no vendor in the loop. For a category of builders who used to be priced out of meaningful AI infrastructure, that is the entire point.&lt;/p&gt;

&lt;p&gt;More posts in this series — including the honest 4-bit vs 8-bit benchmark numbers, the sectional generation pattern in detail, and the 12-agent priority-queue design — live in the &lt;a href="https://sleepyquant.rest/blog/" rel="noopener noreferrer"&gt;SleepyQuant blog archive&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Disclaimer:&lt;/strong&gt; This post is engineering observation, not financial or hardware purchasing advice. Specific tokens-per-second numbers reflect the SleepyQuant configuration on one M1 Max with 64GB unified memory in April 2026; results on other hardware or quantizations will differ. Verify benchmarks against your own workload before making allocation decisions.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>quant</category>
      <category>mlx</category>
      <category>buildinpublic</category>
    </item>
    <item>
      <title>SleepyQuant Weekly · 2026W16</title>
      <dc:creator>Gugubibi</dc:creator>
      <pubDate>Sat, 18 Apr 2026 08:26:56 +0000</pubDate>
      <link>https://dev.to/gugubibi/sleepyquant-weekly-2026w16-1lb0</link>
      <guid>https://dev.to/gugubibi/sleepyquant-weekly-2026w16-1lb0</guid>
      <description>&lt;h2&gt;
  
  
  This week in paper trading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Round-trips: 464&lt;/li&gt;
&lt;li&gt;Win rate: 38.1%&lt;/li&gt;
&lt;li&gt;Realized PnL: -34.58 USDT&lt;/li&gt;
&lt;li&gt;Net return: +20.23%&lt;/li&gt;
&lt;li&gt;Max drawdown: 3.14%&lt;/li&gt;
&lt;li&gt;R:R ratio: 0.8&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Failure vault: what broke, what changed
&lt;/h2&gt;

&lt;p&gt;Past 7 days · 49 losing trades · total -24.63 USDT&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Execution Slippage&lt;/strong&gt; cluster × 25 across APT/USDT, BNB/USDT, ETH/USDT, LINK/USDT&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Technical Failure&lt;/strong&gt; cluster × 24 across APT/USDT, ARB/USDT, ATOM/USDT, AVAX/USDT&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;APT/USDT&lt;/strong&gt; — Execution Slippage × 5 (-1.32 USDT, avg -0.26 per trade)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Strategy adjustments shipped / queued for next week:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;[65% conf]&lt;/strong&gt; Scanner-wide: cut position size 25% + tighten stop loss&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;[60% conf]&lt;/strong&gt; Global: scan interval 8 → 12 minutes to filter noise&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;[85% conf]&lt;/strong&gt; Temporarily remove APT/USDT from scan list for 48 hours&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  News that mattered
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;🔥 Trending: Bio Protocol (BIO) — Rank #365 &lt;em&gt;(via CoinGecko Trending)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;🔥 Trending: Pudgy Penguins (PENGU) — Rank #108 &lt;em&gt;(via CoinGecko Trending)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;🔥 Trending: RaveDAO (RAVE) — Rank #33 &lt;em&gt;(via CoinGecko Trending)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;🔥 Trending: Based (BASED) — Rank #722 &lt;em&gt;(via CoinGecko Trending)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;🔥 Trending: Bitcoin (BTC) — Rank #1 &lt;em&gt;(via CoinGecko Trending)&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  One operating insight
&lt;/h2&gt;

&lt;p&gt;The main lesson this week is simple: trust the quiet tape.&lt;/p&gt;

&lt;p&gt;When the engine scans widely but trades narrowly, that usually means the filters are doing their job. A lower trade count is cheaper than forcing mediocre entries, especially when the failure vault is already pointing at repeat mistakes like noisy confirmation, weak follow-through, or execution drift. The right response is not "make the bot trade more." The right response is to tighten the decision path, preserve RAM for the live stack, and keep publishing the real numbers so the system can keep learning in public.&lt;/p&gt;




&lt;h2&gt;
  
  
  Stack and infra
&lt;/h2&gt;

&lt;p&gt;The stack right now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Apple M1 Max, 64GB unified memory&lt;/li&gt;
&lt;li&gt;MLX Qwen 3.6 35B-A3B 8-bit quant (primary inference)&lt;/li&gt;
&lt;li&gt;A lightweight CLI layer for build-time automation&lt;/li&gt;
&lt;li&gt;12 AI agents coordinating in one local process&lt;/li&gt;
&lt;li&gt;Binance spot + futures paper trading via ccxt&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The model swap from 4-bit to 8-bit this week traded raw decode speed (about 50 tokens per second down to about 10) for sharper data-aware evaluation. Worthwhile for content quality scoring; less worthwhile for high-frequency scan loops, which still rely on cached deterministic signals.&lt;/p&gt;




&lt;p&gt;If you're building local-first trading systems, hit reply and tell me what you optimize for first: speed, cost, or control. The next issue covers the inverted-control experiment: running the same signal backward on a parallel paper book to test whether the edge is real or whether the bot is anti-correlated with itself.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Compiled from live operating data. Every number in this issue came from the running system, not a deck.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>quant</category>
      <category>mlx</category>
      <category>buildinpublic</category>
    </item>
    <item>
      <title>The Inverted Control: What 24 Hours of Running Our Own Bot Backwards Revealed</title>
      <dc:creator>Gugubibi</dc:creator>
      <pubDate>Sat, 18 Apr 2026 03:37:25 +0000</pubDate>
      <link>https://dev.to/gugubibi/the-inverted-control-what-24-hours-of-running-our-own-bot-backwards-revealed-402g</link>
      <guid>https://dev.to/gugubibi/the-inverted-control-what-24-hours-of-running-our-own-bot-backwards-revealed-402g</guid>
      <description>&lt;h1&gt;
  
  
  The Inverted Control: What 24 Hours of Running Our Own Bot Backwards Revealed
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Executive Summary
&lt;/h2&gt;

&lt;p&gt;After roughly 500 paper round-trips showed a persistent sub-35% win rate with average losses larger than average wins, we stopped scaling the live side and ran a cheap experiment: a second paper book that executes the exact opposite of every signal the bot produces, on the same universe, same cadence, same fee model.&lt;/p&gt;

&lt;p&gt;Twenty-four hours in, the inverted book is winning 70.59% of round-trips versus 15.79% on the standard book. Both books are still losing in absolute terms because fees dominate at small sample. The important number is not the win rate gap. It is whether the inverted book's gross edge clears the fee floor by the time we hit the 100-round-trip decision point, roughly 8 to 12 days out.&lt;/p&gt;

&lt;p&gt;This post walks through the setup, the data so far, where the reading could be wrong, and the specific decision that happens at 100 round-trips.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Thesis
&lt;/h2&gt;

&lt;p&gt;A bot that loses more than random is either extracting no signal, or extracting signal with the sign reversed. Those two hypotheses produce identical win-rate readings in a one-book world. They are only separable by running a second book with the signal flipped.&lt;/p&gt;

&lt;p&gt;The second hypothesis is rarer but well-documented: overfit features trained on stale microstructure, labels that got reversed in a pipeline step, crowding where yesterday's "bullish" marker is now a faded trade. None of those are visible from inside a single losing book. All of them flip sign when you flip the signal.&lt;/p&gt;

&lt;p&gt;Running the inverted control is the lowest-cost diagnostic that distinguishes the two hypotheses. In the first hypothesis (no signal), the inverted book converges to the same losing distribution, minus fee drag. In the second hypothesis (inverted signal), the inverted book diverges: higher win rate, smaller loss magnitude, possibly net-positive once sample grows past fee-drag territory.&lt;/p&gt;

&lt;p&gt;The point of running the control is not to find a winning strategy. It is to stop guessing about which of those two worlds the bot is actually in.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Setup
&lt;/h2&gt;

&lt;p&gt;Two paper books, same engine, same universe, same fee schedule.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Book 1 — standard signal.&lt;/strong&gt; Every decision from the scanner is executed as issued. LONG is LONG, BUY is BUY.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Book 2 — inverted mirror.&lt;/strong&gt; Every decision is flipped programmatically before execution. LONG becomes SHORT, BUY becomes SELL (or hold, since the spot lane is accumulate-only during this window, making the flip mostly a futures test).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both books start from identical simulated ~$1000 balances. Both pay realistic exchange-tier fees on open and close — no free-trade assumption, which is where most inversion backtests fail.&lt;/p&gt;

&lt;p&gt;Universe: 30 USDT pairs on a major exchange, perps plus spot. Scan cadence 15 minutes. Leverage cap 3x. Drawdown hard stop 8% per book. Spot exit signals ignored in Book 2 for this window — the test isolates the futures direction bet.&lt;/p&gt;

&lt;p&gt;The test completes at 100 post-flip round-trips on Book 2. At that point one of three decisions is on the table.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deep Dive: 24 Hours of Parallel Data
&lt;/h2&gt;

&lt;p&gt;Windowed to the period since the flip went live:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Book 1 — standard.&lt;/strong&gt; 38 round-trips closed. Win rate 15.79%. Net result negative on the order of tens of USD.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Book 2 — inverted.&lt;/strong&gt; 17 round-trips closed. Win rate 70.59%. Net result also negative, but by a much smaller per-round-trip magnitude (roughly 25x better than standard).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The win-rate gap from 15.79% to 70.59% is the headline. It is not a statistical fluke at this sample. A purely random signal in this setup would produce win rates clustering around 45-55% on both books. A noise signal (first hypothesis) would produce roughly symmetric rates on both books. What shows up instead — asymmetric split heavily favoring the inverse — is the fingerprint of a signal that carries information with the wrong sign.&lt;/p&gt;

&lt;p&gt;Per-symbol, the inversion's effect is not uniform:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symbol&lt;/th&gt;
&lt;th&gt;Book 1 WR&lt;/th&gt;
&lt;th&gt;Book 2 WR&lt;/th&gt;
&lt;th&gt;Direction&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ZEC/USDT&lt;/td&gt;
&lt;td&gt;12.5% (8 RT)&lt;/td&gt;
&lt;td&gt;80.0% (5 RT)&lt;/td&gt;
&lt;td&gt;Inversion strongly helps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ARB/USDT&lt;/td&gt;
&lt;td&gt;25.0% (4 RT)&lt;/td&gt;
&lt;td&gt;100% (3 RT)&lt;/td&gt;
&lt;td&gt;Inversion helps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DOGE/USDT&lt;/td&gt;
&lt;td&gt;0.0% (5 RT)&lt;/td&gt;
&lt;td&gt;100% (2 RT)&lt;/td&gt;
&lt;td&gt;Inversion helps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UNI/USDT&lt;/td&gt;
&lt;td&gt;0.0% (4 RT)&lt;/td&gt;
&lt;td&gt;100% (1 RT)&lt;/td&gt;
&lt;td&gt;Inversion helps (micro sample)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BCH/USDT&lt;/td&gt;
&lt;td&gt;0.0% (1 RT)&lt;/td&gt;
&lt;td&gt;100% (1 RT)&lt;/td&gt;
&lt;td&gt;Inversion helps (micro sample)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NEAR/USDT&lt;/td&gt;
&lt;td&gt;28.6% (7 RT)&lt;/td&gt;
&lt;td&gt;0.0% (2 RT)&lt;/td&gt;
&lt;td&gt;Inversion hurts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ADA/USDT&lt;/td&gt;
&lt;td&gt;50.0% (4 RT)&lt;/td&gt;
&lt;td&gt;33.3% (3 RT)&lt;/td&gt;
&lt;td&gt;Inversion hurts&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Five of seven symbols with both-book data favor inversion. Two do not. The symbols where inversion fails are the ones where the standard book was already near or above 30% — consistent with a "invert only what's clearly broken, leave the rest" hybrid strategy that may emerge at higher sample.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Fee Floor
&lt;/h3&gt;

&lt;p&gt;Every round-trip pair costs roughly the open-plus-close fee on a major exchange, applied to both books independently. With Book 2 running in parallel, fees double.&lt;/p&gt;

&lt;p&gt;That doubles the bar. Book 2's improvement in gross profit-and-loss has to clear two fee stacks, not one. An inversion signal that wins on gross but gets eaten by the fee floor is a classic mean-reversion trap: backtests ignoring fees look clean, live books ignoring fees bleed out.&lt;/p&gt;

&lt;p&gt;At 17 round-trips, Book 2's net-negative result is dominated by fee drag, not by losses on individual trades. The interesting question is whether that fee drag, as a percentage of gross result, shrinks as sample grows. If the gross per-round-trip edge holds at roughly current magnitude, net-positive becomes plausible around round-trip 50-70. If the gross edge compresses as the signal gets noisier at larger sample, net-positive never arrives.&lt;/p&gt;

&lt;h2&gt;
  
  
  Counter-Argument: Why This Reading Could Be Wrong
&lt;/h2&gt;

&lt;p&gt;Taking the opposite side of our own preliminary conclusion:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sample is too small.&lt;/strong&gt; Seventeen round-trips on Book 2 is the sample size a drunk person at a blackjack table has after twenty minutes. Win-rate distributions at n=17 are wide enough that a 70.59% result can reverse to 35% over the next 30 trips without surprising anyone. Any reading here is provisional.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recent regime shift.&lt;/strong&gt; The standard book's historical 34% win rate was compiled over weeks. The 15.79% since the flip is over 24 hours. A regime change (one market day of trend-heavy action on symbols the scanner dislikes, for example) could compress the standard book's rate artificially without the underlying signal being any more broken than it was a week ago. That would make the inversion's apparent edge a mirage of timing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Asymmetric fee burn.&lt;/strong&gt; Book 2's inverted futures positions may open and close in ways that pay funding rate differently than Book 1's. If the test period coincides with a funding regime that favors one side, some of the apparent gross edge is just "Book 2 happened to be on the right side of funding this week."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The symbols where inversion fails are the ones we actually trade most.&lt;/strong&gt; The test might reveal that inversion works on low-activity symbols that produce little volume, while the symbols driving Book 1's meaningful losses (higher-sample names like BTC, ETH, SOL, which Book 2 has not yet traded in this window) are not in the inverted-signal camp. A strategy that only works on low-volume names is not a strategy worth running.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The signal might be improving organically.&lt;/strong&gt; Book 1's live standard-signal win rate (across all history, not just this window) has been creeping toward 34% from the 27% it hit in the worst stretch earlier in April. If the signal is already self-correcting, the inversion's apparent edge evaporates before the test window closes.&lt;/p&gt;

&lt;p&gt;Any one of those could be what is actually going on. We are not going to know until the sample grows.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Verdict
&lt;/h2&gt;

&lt;p&gt;The decision point is 100 round-trips on Book 2, expected 8 to 12 days out.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If Book 2 lands net-positive with win rate above 55%:&lt;/strong&gt; the inversion locks in. The live signal gets flipped permanently, along with the take-profit and stop-loss asymmetry (swap from 3% TP / -2% SL to 2% TP / -3% SL to match the inverted payoff shape). Live trading remains paused until the paper side clears a 30-day rolling benchmark of Binance Simple Earn at roughly 0.42% per month — the honest passive bar.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If Book 2 lands net-negative or drawdown exceeds 8%:&lt;/strong&gt; the futures lane is disabled entirely. Spot accumulation remains. The diagnosis shifts from "inverted signal" to "no signal," and the rebuild restarts on features, not direction.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If Book 2 lands mixed — gross positive but net-negative, or win rate high but below 55%:&lt;/strong&gt; the hybrid path becomes the next experiment. Invert only the symbols where Book 1's rolling win rate sits below 40%. Leave the ones above 40% standard. Re-run the control on that subset.&lt;/p&gt;

&lt;h3&gt;
  
  
  What the reader should take from this
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;If you are running a paper book that loses more than random:&lt;/strong&gt; run the inverted control before killing the strategy. The setup is one column in the trades table (&lt;code&gt;book_id&lt;/code&gt;) and one branch in the execute function. Cost is near zero, answer is binary, information is much larger than the cost.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you are watching SleepyQuant for the outcome:&lt;/strong&gt; the result arrives at 100 round-trips. We publish either a "inversion locks in, here is the updated config" or a "futures lane disabled, here is why" — whichever the numbers say, not whichever is more flattering.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you are here for the general lesson:&lt;/strong&gt; a losing signal is not automatically noise. Sometimes it is a working signal with the sign reversed. The diagnostic is cheap. The implication — that your model has been right about structure and wrong about direction — is unusual enough that most builders never check. The check itself is worth more than the result.&lt;/p&gt;

&lt;h2&gt;
  
  
  Follow the experiment
&lt;/h2&gt;

&lt;p&gt;We publish one email per week with the round-trip count, the current win rates on both books, the fee-drag ratio, and whatever the honest read is at that point. No trading advice, no signals, no "buy at X." Just the numbers and what we are and are not willing to conclude from them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Subscribe at &lt;a href="https://sleepyquant.rest" rel="noopener noreferrer"&gt;sleepyquant.rest&lt;/a&gt;&lt;/strong&gt; → the verdict lands in your inbox.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>quant</category>
      <category>mlx</category>
      <category>buildinpublic</category>
    </item>
    <item>
      <title>Show HN: SleepyQuant – a 12-agent crypto quant running on one Mac</title>
      <dc:creator>Gugubibi</dc:creator>
      <pubDate>Sat, 18 Apr 2026 01:47:25 +0000</pubDate>
      <link>https://dev.to/gugubibi/show-hn-sleepyquant-a-12-agent-crypto-quant-running-on-one-mac-4dhh</link>
      <guid>https://dev.to/gugubibi/show-hn-sleepyquant-a-12-agent-crypto-quant-running-on-one-mac-4dhh</guid>
      <description>&lt;h1&gt;
  
  
  Show HN: SleepyQuant – a 12-agent crypto quant running on one Mac
&lt;/h1&gt;

&lt;p&gt;Hey everyone,&lt;/p&gt;

&lt;p&gt;SleepyQuant is a solo experiment I've been running for the last couple of weeks: 12 local AI agents coordinating a paper crypto trading book on a single Apple M1 Max. No cloud inference, no API bills, no vendor black box. Every agent prompt, every losing trade, every round-trip gets written up weekly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stack (all local):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Apple M1 Max, 64 GB RAM&lt;/li&gt;
&lt;li&gt;MLX Qwen 2.5 32B Q8 as the primary agent model&lt;/li&gt;
&lt;li&gt;DeepSeek R1 14B Q8 as a lazy-loaded reasoning lane for research tasks&lt;/li&gt;
&lt;li&gt;Priority queue on the MLX inference lock so user chat preempts automation&lt;/li&gt;
&lt;li&gt;FastAPI backend, SwiftUI macOS app, SQLite for state, ChromaDB for agent memory&lt;/li&gt;
&lt;li&gt;Binance paper via ccxt, spot + futures, 70/30 allocation, 10x leverage on the futures lane&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What's deliberately boring:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The paper book is roughly $78 equivalent. Not a typo. The real-mode transition gate requires three consecutive green days before anything touches real capital, and even then the first real trade is capped tiny. If the strategy can't handle $78, I'd rather find out for free.&lt;/li&gt;
&lt;li&gt;Tight scalp TP/SL (2.0% / -1.5% on futures) with a hard -8% daily drawdown stop.&lt;/li&gt;
&lt;li&gt;Every losing trade gets a post-mortem. The failure vault is public in the weekly newsletter, with root-cause classification (technical / news / execution slippage) and the exact param changes shipped as a response.&lt;/li&gt;
&lt;li&gt;Funding rate guard — refuses to open futures positions when our side is paying extreme funding. Shipped after the scanner was quietly bleeding basis points for three days straight.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Agents (one role each):&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A COO / dispatcher, a trading lead, separate futures + spot executors, a CFO, a CTO with filesystem + shell tools, an R&amp;amp;D / failure analyst, a legal / compliance officer, a resource monitor, a QA engineer, a news intelligence watcher, and a content / SEO writer.&lt;/p&gt;

&lt;p&gt;Each agent has a focused system prompt + a small set of skill handlers. The COO routes CEO requests to the right specialist instead of one monolithic agent trying to do everything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Live paper P&amp;amp;L widget + weekly newsletter:&lt;/strong&gt; &lt;a href="https://sleepyquant.rest" rel="noopener noreferrer"&gt;https://sleepyquant.rest&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Two things I'd genuinely want feedback on — please weigh in below:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Is 12 agents worth the routing overhead?&lt;/strong&gt; Or would a single bigger agent with tool use be cleaner at this scale? I keep flip-flopping and would love to hear from anyone who's been through the same decomposition choice.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;MLX unload strategies on Apple Silicon?&lt;/strong&gt; Right now my reasoning model auto-unloads after 2 minutes idle, which works but feels crude. If you're running MLX in production on a Mac, how do you free RAM when you need it back?&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Try it or follow along:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Live paper P&amp;amp;L widget + weekly write-up:&lt;/strong&gt; &lt;a href="https://sleepyquant.rest" rel="noopener noreferrer"&gt;https://sleepyquant.rest&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subscribe to the weekly post-mortem newsletter&lt;/strong&gt; — Beehiiv, free, one email per week, no upsells, no signals, no affiliate links&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cadence:&lt;/strong&gt; every Tuesday. If the book dies, I'll write up that too&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Happy to answer questions in the comments about the architecture, the failure vault, the priority queue design, or why local-first LLM agents are worth the effort on a 64 GB machine. Fire away.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>quant</category>
      <category>mlx</category>
      <category>buildinpublic</category>
    </item>
    <item>
      <title>SleepyQuant — Twitter brand assets (bio + pinned tweet)</title>
      <dc:creator>Gugubibi</dc:creator>
      <pubDate>Sat, 18 Apr 2026 01:47:21 +0000</pubDate>
      <link>https://dev.to/gugubibi/sleepyquant-twitter-brand-assets-bio-pinned-tweet-1joe</link>
      <guid>https://dev.to/gugubibi/sleepyquant-twitter-brand-assets-bio-pinned-tweet-1joe</guid>
      <description>&lt;h2&gt;
  
  
  Profile
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Display name (50 chars max):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SleepyQuant
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Bio (160 chars max — landing + newsletter + 1-line pitch):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AI trades while the CEO sleeps. 12 local agents + one Mac M1 Max running a paper crypto book in public. Weekly post-mortems, zero hype.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(139 chars — room for a trailing link to sleepyquant.rest in the website field rather than in the bio text itself.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Location:&lt;/strong&gt; &lt;code&gt;Runs on a Mac in a closet&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Website:&lt;/strong&gt; &lt;code&gt;https://sleepyquant.rest&lt;/code&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Pinned tweet
&lt;/h2&gt;

&lt;p&gt;One tweet, no thread. Meant to be the first thing a new visitor sees. No question on purpose — it's a brand statement, not a conversation opener.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;One Mac. 12 AI agents. A $78 paper crypto book.

I run a quant experiment while I sleep and post the whole journey — every win, every dumb loss, every architecture note — every week.

Live P&amp;amp;L + the newsletter: sleepyquant.rest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(278 chars — right under the 280 limit, no line-break tricks, reads in one pass.)&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Alternate pinned tweet (if the first one feels too cold)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;I ship weekly regardless of wins or losses.

Week 1 on paper: +2.65%, 9 round-trips, 3 losses with full post-mortems, funding-rate guard shipped mid-week.

Everything runs locally on one Mac. sleepyquant.rest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(242 chars.)&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Notes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Keep the bio and pinned tweet aligned on tone. Reader should see the bio, then the pinned, and the two should feel like one voice.&lt;/li&gt;
&lt;li&gt;Don't use "crypto trading bot" — implies signals and gets flagged by X ad policy. Use "paper crypto book" or "quant experiment".&lt;/li&gt;
&lt;li&gt;Update the pinned weekly — roll in the latest round-trip number so it never feels stale. The alternate version is a good template for that weekly refresh.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>quant</category>
      <category>mlx</category>
      <category>buildinpublic</category>
    </item>
  </channel>
</rss>
