<?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: Supreet Sethi</title>
    <description>The latest articles on DEV Community by Supreet Sethi (@djinn).</description>
    <link>https://dev.to/djinn</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%2F2892366%2F7e430096-b2c8-4869-8385-c5eb140aed82.jpg</url>
      <title>DEV Community: Supreet Sethi</title>
      <link>https://dev.to/djinn</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/djinn"/>
    <language>en</language>
    <item>
      <title>Why Stockfish is So Good (and How You Could Write a Chess Engine)</title>
      <dc:creator>Supreet Sethi</dc:creator>
      <pubDate>Thu, 19 Feb 2026 01:18:54 +0000</pubDate>
      <link>https://dev.to/djinn/why-stockfish-is-so-good-and-how-you-could-write-a-chess-engine-2lck</link>
      <guid>https://dev.to/djinn/why-stockfish-is-so-good-and-how-you-could-write-a-chess-engine-2lck</guid>
      <description>&lt;p&gt;Stockfish 18 plays chess at 3759 Elo—far beyond the strongest humans ever lived. But it's open-source, written in about 50,000 lines of C++, and runs on any laptop. This article surveys how modern chess engines work, why NNUE neural networks changed everything, and most importantly: the design decisions you'd make if you wanted to build one yourself.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Tournament Results Tell the Story
&lt;/h2&gt;

&lt;p&gt;Every engine in the top 10 worldwide is now open-source. The proprietary era ended. On the CCRL (Chess Computer Rating List) 40/15 time control, the 2025 hierarchy looks like this:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Rank&lt;/th&gt;
&lt;th&gt;Engine&lt;/th&gt;
&lt;th&gt;Elo&lt;/th&gt;
&lt;th&gt;Language&lt;/th&gt;
&lt;th&gt;Evaluation&lt;/th&gt;
&lt;th&gt;Open Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Stockfish 18&lt;/td&gt;
&lt;td&gt;3650&lt;/td&gt;
&lt;td&gt;C++&lt;/td&gt;
&lt;td&gt;NNUE&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;PlentyChess 7.0&lt;/td&gt;
&lt;td&gt;3643&lt;/td&gt;
&lt;td&gt;C++&lt;/td&gt;
&lt;td&gt;NNUE&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Obsidian 16.0&lt;/td&gt;
&lt;td&gt;3635&lt;/td&gt;
&lt;td&gt;C++&lt;/td&gt;
&lt;td&gt;NNUE&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Alexandria 8&lt;/td&gt;
&lt;td&gt;3633&lt;/td&gt;
&lt;td&gt;Rust&lt;/td&gt;
&lt;td&gt;NNUE&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Viridithas 19&lt;/td&gt;
&lt;td&gt;3629&lt;/td&gt;
&lt;td&gt;Rust&lt;/td&gt;
&lt;td&gt;NNUE&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Berserk 13&lt;/td&gt;
&lt;td&gt;3615&lt;/td&gt;
&lt;td&gt;C&lt;/td&gt;
&lt;td&gt;NNUE&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;Leela Chess Zero&lt;/td&gt;
&lt;td&gt;~3443&lt;/td&gt;
&lt;td&gt;C++&lt;/td&gt;
&lt;td&gt;Deep NN&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Leela Chess Zero is roughly 200 Elo weaker than Stockfish despite using a 191-million-parameter transformer neural network, simply because Stockfish can evaluate 1,500× more positions per second. Speed matters more than brilliance in chess.&lt;/p&gt;




&lt;h2&gt;
  
  
  NNUE Was The Breakthrough Nobody Expected
&lt;/h2&gt;

&lt;p&gt;In August 2020, Stockfish introduced NNUE (Efficiently Updatable Neural Networks), originally invented for computer shogi. The jump was &lt;strong&gt;+80 to +100 Elo overnight&lt;/strong&gt;—equivalent to two years of conventional development. Every top engine today uses NNUE or a variation of it.&lt;/p&gt;

&lt;p&gt;The genius of NNUE is that it's specialized for the constraints of chess engines:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sparse activation&lt;/strong&gt;: Only ~30 of 40,960 input features are active per position (99.93% dormant). Most neural network activations are wasted computation in the context of alpha-beta search.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Incremental updates&lt;/strong&gt;: Since only 2-4 features change per move, you don't recompute the entire network—you update an "accumulator" in place. This makes evaluation sub-microsecond.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Integer arithmetic&lt;/strong&gt;: NNUE uses int8 and int16 quantization, optimized for CPU SIMD (AVX2, AVX-512). No float32 overhead. Roughly &lt;strong&gt;60 million evaluations per second&lt;/strong&gt; on a modern CPU.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Stockfish's current NNUE uses a 1024×2→8→32→1 architecture (inputs expand to 1024, then compress through small hidden layers). The network is trained on billions of self-play positions scored at modest search depths, using PyTorch with custom CUDA kernels.&lt;/p&gt;

&lt;p&gt;The completely hand-crafted evaluation function—Stockfish's secret sauce for 15 years—was &lt;strong&gt;deleted entirely in 2023&lt;/strong&gt;. Now NNUE does everything.&lt;/p&gt;




&lt;h2&gt;
  
  
  Two Paradigms Compete: Alpha-Beta vs. Monte Carlo Tree Search
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Alpha-Beta Search with NNUE (Stockfish's Approach)
&lt;/h3&gt;

&lt;p&gt;Stockfish searches a tree systematically, pruning branches that cannot improve the best move. It uses &lt;strong&gt;Principal Variation Search (PVS)&lt;/strong&gt;, a variant of alpha-beta that searches the expected best move with a full window and all others with a zero-width window.&lt;/p&gt;

&lt;p&gt;The trick is aggressive pruning without missing critical moves:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Late Move Reductions (LMR)&lt;/strong&gt;: Moves ordered later are searched at reduced depth. Estimated &lt;strong&gt;+100 Elo&lt;/strong&gt; when first introduced.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Null Move Pruning&lt;/strong&gt;: If passing (making no move) already produces a cutoff, prune the subtree. Dangerous in zugzwang, but nets &lt;strong&gt;+100-200 Elo&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Singular Extensions&lt;/strong&gt;: If one move dramatically outperforms all others, search it deeper.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Futility Pruning&lt;/strong&gt;: At shallow depths, positions too far below alpha are pruned immediately.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SEE Pruning&lt;/strong&gt;: Moves with losing material exchanges are pruned based on static exchange evaluation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These techniques shrink the effective branching factor from ~35 to less than 2, enabling search to depth 30-40 plies where NNUE provides the final judgment.&lt;/p&gt;

&lt;h3&gt;
  
  
  MCTS with Deep Neural Networks (Leela Chess Zero)
&lt;/h3&gt;

&lt;p&gt;Leela Chess Zero uses Monte Carlo Tree Search combined with a 191-million-parameter transformer. Instead of exhaustively searching and pruning, it builds a search tree by repeatedly:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Selecting&lt;/strong&gt; a node via the PUCT formula: &lt;code&gt;Q(s,a) + c_puct × P(s,a) × √(N_parent) / (1 + N(s,a))&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Evaluating&lt;/strong&gt; with the neural network (value head), which replaces classical random rollouts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backing up&lt;/strong&gt; results to update statistics&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The network provides both a &lt;strong&gt;policy head&lt;/strong&gt; (prior probability for each move) and a &lt;strong&gt;value head&lt;/strong&gt; (win probability), replacing the random playout entirely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The outcome&lt;/strong&gt;: MCTS's deep NN produces higher-quality per-position judgments, but Stockfish compensates by evaluating vastly more positions per second. On TCEC (the engine championship), Stockfish beats Leela roughly 57-43 and has won every superfinal since 2020. The gap appears to be widening.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why C++ (And Sometimes Rust)
&lt;/h2&gt;

&lt;p&gt;Programming language choice directly impacts Elo rating at the elite level. Each additional ply of search adds roughly &lt;strong&gt;50-70 Elo&lt;/strong&gt;. A 2× difference in nodes-per-second (NPS) means one extra ply—so language choice matters.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Language&lt;/th&gt;
&lt;th&gt;Top Engine&lt;/th&gt;
&lt;th&gt;Elo&lt;/th&gt;
&lt;th&gt;NPS Relative to C++&lt;/th&gt;
&lt;th&gt;Elo Cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;C++&lt;/td&gt;
&lt;td&gt;Stockfish&lt;/td&gt;
&lt;td&gt;3759&lt;/td&gt;
&lt;td&gt;Baseline&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C&lt;/td&gt;
&lt;td&gt;Berserk&lt;/td&gt;
&lt;td&gt;3646&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rust&lt;/td&gt;
&lt;td&gt;Alexandria&lt;/td&gt;
&lt;td&gt;3633&lt;/td&gt;
&lt;td&gt;95-100%&lt;/td&gt;
&lt;td&gt;0-5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C#&lt;/td&gt;
&lt;td&gt;Ceres&lt;/td&gt;
&lt;td&gt;~3624&lt;/td&gt;
&lt;td&gt;70-80%&lt;/td&gt;
&lt;td&gt;30-60&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Java&lt;/td&gt;
&lt;td&gt;chess22k&lt;/td&gt;
&lt;td&gt;~3000&lt;/td&gt;
&lt;td&gt;60-80%&lt;/td&gt;
&lt;td&gt;30-60&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Python&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;~1800&lt;/td&gt;
&lt;td&gt;1-2%&lt;/td&gt;
&lt;td&gt;400+&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;C and C++ dominate because they offer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Direct hardware access (cache control, SIMD intrinsics)&lt;/li&gt;
&lt;li&gt;No garbage collection pauses&lt;/li&gt;
&lt;li&gt;Compile-time optimizations&lt;/li&gt;
&lt;li&gt;Fast bitboard operations (64-bit integers for board representation)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Rust is a surprise win.&lt;/strong&gt; Alexandria and Viridithas prove that Rust—with its zero-cost abstractions and LLVM compilation—is fully competitive at the elite level. Move generation, board representation, and evaluation all run at near-C++ speeds.&lt;/p&gt;

&lt;p&gt;Managed languages like C# and Java incur 30-60 Elo penalties from runtime overhead and garbage collection. Below ~3200 Elo, this doesn't matter—algorithmic skill outweighs implementation. But above 3600, the language tax is disqualifying.&lt;/p&gt;




&lt;h2&gt;
  
  
  Open-Source Development Beats Proprietary
&lt;/h2&gt;

&lt;p&gt;Twelve of the top 15 engines are open-source. This wasn't ideological—it was mechanistic.&lt;/p&gt;

&lt;p&gt;In 2013, Gary Linscott created &lt;strong&gt;Fishtest&lt;/strong&gt;, a distributed testing framework for Stockfish using Sequential Probability Ratio Testing (SPRT). Volunteers donate CPU time. The numbers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;19,700+ CPU-years consumed&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;9.8+ billion games played&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;~199 concurrent worker machines&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;For Stockfish 15: ~13,000 candidate changes tested, ~200 merged&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Stockfish gained &lt;strong&gt;+120 Elo in the first year of Fishtest&lt;/strong&gt;—a pace no proprietary team could match. Andrew Grant's &lt;strong&gt;OpenBench&lt;/strong&gt; extended this framework to 17+ other engines (Berserk, Koivisto, Stormphrax, Viridithas, etc.).&lt;/p&gt;

&lt;p&gt;The reason is simple: you cannot optimize an engine in isolation. Testing every patch requires millions of games. Distributed testing at cloud scale is the only way to move fast. Open-source enabled this. Proprietary teams struggle to match it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Other Key Technical Insights
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Lazy SMP (Shared Memory Parallelization)&lt;/strong&gt;: Going from 1 to 16 threads adds only ~20-32 Elo, far less than the theoretical benefit. Engines are memory-bound, not CPU-bound, at high core counts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Syzygy Tablebases&lt;/strong&gt;: 6-man endgame databases provide ~10 Elo improvement by allowing perfect play in simplified positions. Stockfish ships with 6-man tablebases.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Depth vs. NNUE Quality&lt;/strong&gt;: Modern NNUE networks are compact (several MB), but the old hand-crafted evaluations were more "reasonable" locally. Deeper search compensates: Stockfish searches to 40+ plies where NNUE makes the final judgment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Each Additional Ply&lt;/strong&gt;: Worth approximately &lt;strong&gt;+50-70 Elo&lt;/strong&gt; across all engines. Doubling NPS ≈ one extra ply ≈ 50-70 Elo.&lt;/p&gt;




&lt;h2&gt;
  
  
  If You Want To Build a Chess Engine: Design Sweet Spots
&lt;/h2&gt;

&lt;p&gt;Most programmers who build chess engines do it for fun or learning. Here's a realistic roadmap with reasonable design trade-offs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tier 1: A Respectable Hobbyist Engine (1400-1800 Elo)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal&lt;/strong&gt;: Beat humans, work on a laptop, finish in a weekend.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Design choices:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Language&lt;/strong&gt;: Python or Rust (you care about learning, not top-10 rankings)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Search&lt;/strong&gt;: Basic alpha-beta with minimal pruning (null move pruning is the only essential trick)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Evaluation&lt;/strong&gt;: Hand-crafted. A simple function counting material + positional bonuses for piece centralization, king safety, pawn advancement&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Board&lt;/strong&gt;: 8×8 2D array (don't use bitboards yet; they're an optimization)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Move generation&lt;/strong&gt;: Brute-force checking all 64 squares&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Expected Elo&lt;/strong&gt;: 1400-1800 (beats most casual players)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Time to implement&lt;/strong&gt;: ~40-80 lines of core move generation + alpha-beta + evaluation = 200-300 lines total&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="c1"&gt;# Pseudocode for minimum viable chess engine
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;evaluate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;board&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;material&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;count_material&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;board&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;position&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;count_piece_activity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;board&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;material&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;position&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;board&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;depth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;beta&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;depth&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;evaluate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;board&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;move&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;generate_moves&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;board&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;board&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;make_move&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;move&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;board&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;depth&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;beta&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;board&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unmake_move&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;move&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;alpha&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;beta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;  &lt;span class="c1"&gt;# Beta cutoff
&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives you 1600+ Elo with virtually no optimization.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tier 2: A Competitive Amateur Engine (2000-2500 Elo)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal&lt;/strong&gt;: Beat most online opponents, viable on older hardware.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Design choices:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Language&lt;/strong&gt;: C++ or Rust (you need speed now, not just learning)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Search&lt;/strong&gt;: Alpha-beta with:

&lt;ul&gt;
&lt;li&gt;Null move pruning&lt;/li&gt;
&lt;li&gt;Late Move Reductions (LMR) with basic formula: &lt;code&gt;reduction = 1 + ln(depth) × ln(moveNumber)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Move ordering (best moves first)&lt;/li&gt;
&lt;li&gt;Transposition table (hash table caching positions)&lt;/li&gt;
&lt;li&gt;Quiescence search (don't stop at arbitrary depth; handle captures)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Evaluation&lt;/strong&gt;: Still hand-crafted, but richer:

&lt;ul&gt;
&lt;li&gt;Material (pawns=1, knights=3, bishops=3.25, rooks=5, queens=9)&lt;/li&gt;
&lt;li&gt;Piece-square tables (centralized pieces score higher)&lt;/li&gt;
&lt;li&gt;Pawn structure (doubled, isolated, passed pawns)&lt;/li&gt;
&lt;li&gt;King safety&lt;/li&gt;
&lt;li&gt;Mobility (count available squares for each piece)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Board&lt;/strong&gt;: Bitboards (64-bit integers for each piece type)&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Move generation&lt;/strong&gt;: Bit-scanning optimized&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Expected Elo&lt;/strong&gt;: 2000-2500 (beats most club players)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Time to implement&lt;/strong&gt;: 1,000-2,000 lines of well-structured C++&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key optimization&lt;/strong&gt;: Transposition table. Storing and retrieving previously evaluated positions cuts the search tree in half.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;TTEntry&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;uint64_t&lt;/span&gt; &lt;span class="n"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;depth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;flag&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// EXACT, LOWER, UPPER&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;unordered_map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;uint64_t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TTEntry&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;transposition_table&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Board&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;board&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;depth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;beta&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;uint64_t&lt;/span&gt; &lt;span class="n"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;board&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transposition_table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;transposition_table&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;depth&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;depth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;flag&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;EXACT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;flag&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;LOWER&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;flag&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;UPPER&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;beta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;beta&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;alpha&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;beta&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// ... search continues ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Tier 3a: A Strong Club Engine (~3000 lines, 2400-2600 Elo)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal&lt;/strong&gt;: Impressive hobbyist engine; beats most players, playable against titled opponents.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you add to Tier 2:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Iterative deepening&lt;/strong&gt;: Search depth 1, then 2, then 3, etc. Allows precise time management and search stability&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Killer heuristic&lt;/strong&gt;: Track moves that produced cutoffs in sibling nodes; try them early in similar positions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;History heuristic&lt;/strong&gt;: Moves that produced cutoffs anywhere in the tree are tried earlier at shallower depths&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Aspiration windows&lt;/strong&gt;: Search the full depth with a narrow alpha-beta window around the previous iteration's score (much faster when you're right)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Staged move ordering&lt;/strong&gt;: Generate checking moves first, then captures, then quiet moves&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Better evaluation&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;Piece-square tables for all pieces (not just pawns)&lt;/li&gt;
&lt;li&gt;Pawn structure analysis (passed pawns, weak squares)&lt;/li&gt;
&lt;li&gt;King tropism (pieces attacking near the opponent's king)&lt;/li&gt;
&lt;li&gt;Tempo bonus (slight evaluation advantage for the side to move)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Board representation&lt;/strong&gt;: Still bitboards, but optimized move generation using bit-scanning&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected Elo&lt;/strong&gt;: 2400-2600 (beats most club-level players, competitive against titled opponents)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Time to implement&lt;/strong&gt;: ~3,000 lines of well-optimized C++&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code example&lt;/strong&gt; (iterative deepening structure):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;iterative_deepening&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Board&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;board&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;time_ms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;best_move&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;best_score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;chrono&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;system_clock&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;depth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;depth&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;depth&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;board&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;depth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;INFINITY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;INFINITY&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;score_is_mate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// If we found mate, no point searching deeper&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;best_move&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;best_move&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;  &lt;span class="c1"&gt;// Principal variation from this depth&lt;/span&gt;
        &lt;span class="n"&gt;best_score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;elapsed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;chrono&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;system_clock&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;elapsed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;time_ms&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.9&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Stop if we've used 90% of allocated time&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;best_move&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Tier 3b: An Intermediate Engine (~5000 lines, 2800-3200 Elo)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal&lt;/strong&gt;: Seriously competitive; tournaments, online rankings, formidable opponent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you add to Tier 3a:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Singular extensions&lt;/strong&gt;: If one move is dramatically better than siblings, search it one ply deeper&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Futility pruning&lt;/strong&gt;: At shallow depths (1-2 plies from qsearch), prune moves that can't possibly improve alpha&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SEE (Static Exchange Evaluation)&lt;/strong&gt;: Use a fast algorithm to estimate if a capture wins or loses material&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Countermove heuristic&lt;/strong&gt;: Track which move was played at the parent node; moves that counter it get priority&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Quiescence search improvements&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;Only examine captures that win material or give check&lt;/li&gt;
&lt;li&gt;Stop immediately if a "quiet" move (non-capture) is better than alpha&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Transposition table refinements&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;Increased size (16 MB → 256 MB)&lt;/li&gt;
&lt;li&gt;Better replacement strategy (always replace, or replace only weaker entries)&lt;/li&gt;
&lt;li&gt;Age tracking (overwrite older entries)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Advanced evaluation&lt;/strong&gt; (still hand-crafted):

&lt;ul&gt;
&lt;li&gt;Separate evaluation for opening, middlegame, endgame (phase calculation)&lt;/li&gt;
&lt;li&gt;Rook on 7th rank bonus&lt;/li&gt;
&lt;li&gt;Opposite-colored bishop endgames (draw tendency)&lt;/li&gt;
&lt;li&gt;Pawn promotion threats&lt;/li&gt;
&lt;li&gt;Mobility counts refined by piece type&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Search enhancements&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Multi-threaded search&lt;/strong&gt; (Lazy SMP): 2-4 threads with shared transposition table (adds ~20 Elo but improves responsiveness)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mate distance pruning&lt;/strong&gt;: If we've found mate in N moves, prune branches that can't improve it&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Null move window refinements&lt;/strong&gt;: Adapt null move reduction based on position criticality (zugzwang detection)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Board representation&lt;/strong&gt;: Bitboards with magic multiplication for bishop/rook attacks (O(1) attack generation)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected Elo&lt;/strong&gt;: 2800-3200 (serious tournament contender; dominates online)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Time to implement&lt;/strong&gt;: ~5,000 lines of optimized C++&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Critical file structure&lt;/strong&gt; at this level:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/
  board.cpp/h        (1,000 lines: bitboards, move gen, fen parsing)
  search.cpp/h       (1,500 lines: alpha-beta, pruning, extensions, TT)
  evaluate.cpp/h     (1,200 lines: handcrafted evaluation)
  movegen.cpp/h      (500 lines: move ordering, sorting)
  uci.cpp/h          (300 lines: UCI protocol interface)
  main.cpp           (100 lines: main loop)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key decision at 5000 lines&lt;/strong&gt;: You've optimized hand-crafted evaluation as far as it goes. To go further, you either:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Fork an open-source NNUE (adds ~1000 lines for integration), or&lt;/li&gt;
&lt;li&gt;Spend months training your own neural network on billions of positions&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Most competitive hobbyist engines stop here or move to Tier 3c.&lt;/p&gt;




&lt;h3&gt;
  
  
  Tier 3c: An Elite Engine (~10,000+ lines, 3000+ Elo)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal&lt;/strong&gt;: Grandmaster territory; competitive in engine championships; serious technical achievement.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you add to Tier 3b:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;NNUE Integration&lt;/strong&gt; (the pivotal decision):

&lt;ul&gt;
&lt;li&gt;Use an existing trained NNUE (most open-source engines do this)&lt;/li&gt;
&lt;li&gt;OR train your own on self-play data using PyTorch&lt;/li&gt;
&lt;li&gt;Integrate int8 NNUE evaluation (~500 lines)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Advanced search techniques&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Razoring&lt;/strong&gt;: Near quiescence, if static eval + margin &amp;gt; alpha, go straight to qsearch (worth ~10-20 Elo)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Internal iterative reduction&lt;/strong&gt;: On the PV, if no best move is available, reduce depth slightly&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Probcut&lt;/strong&gt;: At shallow depths, use a wider evaluation margin to make cutoff predictions&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Time management II&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;Chebyshev time allocation (give more time to critical moves)&lt;/li&gt;
&lt;li&gt;Resignation detection (stop searching if score is lost)&lt;/li&gt;
&lt;li&gt;Contempt factor (slight bias toward fighting positions over draws)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Endgame improvements&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;Syzygy tablebases (6-man endgame databases; ~20-30 MB download, adds ~10 Elo)&lt;/li&gt;
&lt;li&gt;Pawn endgame tables (7-piece, perfect play)&lt;/li&gt;
&lt;li&gt;Bitbase knowledge (e.g., K+P vs K endgame rules)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Evaluation refinements&lt;/strong&gt; (hand-crafted component paired with NNUE):

&lt;ul&gt;
&lt;li&gt;Piece-square table adjustments by material count&lt;/li&gt;
&lt;li&gt;King safety asymmetry (attacker bonus)&lt;/li&gt;
&lt;li&gt;Evaluation tempo bonus (now learned by NNUE, but can add explicit bonuses)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Distributed testing infrastructure&lt;/strong&gt; (if you're serious):

&lt;ul&gt;
&lt;li&gt;Hook into OpenBench or Fishtest&lt;/li&gt;
&lt;li&gt;Enable crowd-sourced testing of your patches&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Search algorithm&lt;/strong&gt;: Principal Variation Search (PVS) with all optimizations, or a modern variant like Negamax&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Threading&lt;/strong&gt;: 4-16 threads with lazy SMP (shared TT, split search at root)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected Elo&lt;/strong&gt;: 3000-3500 (elite level; competitive in engine tournaments)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Time to implement&lt;/strong&gt;: 10,000-15,000 lines of production-quality C++&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Representative code structure&lt;/strong&gt; at this level:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/
  board.cpp/h        (1,200 lines)
  search.cpp/h       (2,500 lines: alpha-beta, all pruning techniques, TT)
  evaluate.cpp/h     (800 lines: NNUE integration + handcrafted bonuses)
  nnue.cpp/h         (800 lines: int8 quantization, accumulator updates)
  movegen.cpp/h      (600 lines)
  uci.cpp/h          (400 lines: UCI options, time management)
  tt.cpp/h           (300 lines: transposition table)
  bitboards.cpp/h    (200 lines: magic bitboards, attacks)
  endgame.cpp/h      (300 lines: tablebases, special rules)
  main.cpp           (100 lines)
  tests/             (2,000 lines: perft, position tests, regression suite)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;NNUE Integration Example&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Simplified NNUE forward pass&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NNUE&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="nl"&gt;public:&lt;/span&gt;
    &lt;span class="kt"&gt;int8_t&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;512&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;    &lt;span class="c1"&gt;// Two perspectives&lt;/span&gt;
    &lt;span class="kt"&gt;int32_t&lt;/span&gt; &lt;span class="n"&gt;hidden&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;       &lt;span class="c1"&gt;// First hidden layer&lt;/span&gt;
    &lt;span class="kt"&gt;int16_t&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;          &lt;span class="c1"&gt;// Final evaluation&lt;/span&gt;

    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;update_accumulator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Move&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Incrementally update instead of full recompute&lt;/span&gt;
        &lt;span class="c1"&gt;// Most of the 1,500× speedup comes from here&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;evaluate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Update accumulator&lt;/span&gt;
        &lt;span class="n"&gt;update_accumulator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;last_move&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Forward pass through tiny network&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;hidden&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;  &lt;span class="c1"&gt;// Simplified&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// Final output layer&lt;/span&gt;
        &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hidden&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;w0&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;hidden&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;w1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;...)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The training pipeline&lt;/strong&gt; (if you're training your own NNUE):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Generate self-play games using your engine
  ↓
Score positions at depth 8-12
  ↓
Collect 1+ billion positions
  ↓
PyTorch training loop (SGD, L1 loss, quantization-aware training)
  ↓
Export to ONNX or custom int8 format
  ↓
Integrate into engine
  ↓
Test on Fishtest/OpenBench
  ↓
If +25 Elo: merge. If -25 Elo: discard.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Maintenance burden at 10K lines:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A comprehensive test suite (perft, mate-in-N puzzles, position regression tests)&lt;/li&gt;
&lt;li&gt;Profiling to identify bottlenecks (search takes ~95% of time; move gen ~2%)&lt;/li&gt;
&lt;li&gt;Continuous tuning: 50+ parameters (LMR reductions, null move depth limits, eval weights) can be optimized with texel tuning or local search&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Where to start&lt;/strong&gt;: Clone Berserk (simpler than Stockfish, ~4,500 lines) and add NNUE support. This gives you a 2.5x head start.&lt;/p&gt;




&lt;h2&gt;
  
  
  Code Complexity vs. Elo Gain (Diminishing Returns)
&lt;/h2&gt;

&lt;p&gt;Here's the brutal truth about chess engine development:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Lines of Code&lt;/th&gt;
&lt;th&gt;Elo&lt;/th&gt;
&lt;th&gt;Time to Build&lt;/th&gt;
&lt;th&gt;Improvement Rate&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;300&lt;/td&gt;
&lt;td&gt;1600&lt;/td&gt;
&lt;td&gt;1 weekend&lt;/td&gt;
&lt;td&gt;+5.3 Elo/line&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1,000&lt;/td&gt;
&lt;td&gt;2000&lt;/td&gt;
&lt;td&gt;1 week&lt;/td&gt;
&lt;td&gt;+0.4 Elo/line&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3,000&lt;/td&gt;
&lt;td&gt;2500&lt;/td&gt;
&lt;td&gt;2-3 weeks&lt;/td&gt;
&lt;td&gt;+0.17 Elo/line&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5,000&lt;/td&gt;
&lt;td&gt;2900&lt;/td&gt;
&lt;td&gt;1-2 months&lt;/td&gt;
&lt;td&gt;+0.08 Elo/line&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10,000&lt;/td&gt;
&lt;td&gt;3300&lt;/td&gt;
&lt;td&gt;3-6 months&lt;/td&gt;
&lt;td&gt;+0.04 Elo/line&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;50,000&lt;/td&gt;
&lt;td&gt;3759&lt;/td&gt;
&lt;td&gt;Years&lt;/td&gt;
&lt;td&gt;+0.01 Elo/line&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;The first 1,000 lines are magical.&lt;/strong&gt; Each line of code adds ~4 Elo. By 5,000 lines, you're down to 0.08 Elo per line. The last 40,000 lines of Stockfish are engineering optimization for marginal gains.&lt;/p&gt;

&lt;p&gt;This is why &lt;strong&gt;starting with 3,000 lines is the sweet spot&lt;/strong&gt; for learning. You get a genuinely strong engine (beats humans), understand all the major algorithmic concepts (alpha-beta, pruning, TT), and can see your work play real chess. Anything beyond 5,000 lines is specialist territory.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Real Lesson
&lt;/h2&gt;

&lt;p&gt;The gap between 1800 Elo and 3600 Elo is about 40 years of accumulated engineering wisdom:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;LMR&lt;/strong&gt; (2004): +100 Elo&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Null move pruning&lt;/strong&gt; (1980s): +150 Elo&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Singular extensions&lt;/strong&gt; (2000s): +30 Elo&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Killer heuristic + history&lt;/strong&gt; (1980s): +50 Elo&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NNUE evaluation&lt;/strong&gt; (2020): +100 Elo&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Iterative deepening + time management&lt;/strong&gt; (1980s): +30 Elo&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each innovation compounds. Modern engines are not singularly brilliant; they're the product of thousands of incremental improvements, tested rigorously on billions of games.&lt;/p&gt;

&lt;p&gt;If you're building a hobbyist engine, focus on &lt;strong&gt;correctness first, optimization second&lt;/strong&gt;. Get move generation and search right before worrying about evaluation. A simple alpha-beta + null move pruning engine at 1600 Elo is a better learning experience than a buggy, half-finished attempt at NNUE.&lt;/p&gt;

&lt;p&gt;If you're aiming for competitive strength, use distributed testing (OpenBench or Fishtest) early. A single developer testing patches locally will trend toward local optima. The open-source ecosystem wins because every improvement is validated against thousands of positions and millions of games.&lt;/p&gt;

&lt;p&gt;And remember: Stockfish is 50,000 lines of C++, but the first 1,000 lines give you 1500 Elo. The remaining 49,000 lines bought maybe 500 more.&lt;/p&gt;




&lt;h2&gt;
  
  
  Further Reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Chessprogramming.org&lt;/strong&gt;: The comprehensive wiki on engine implementation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stockfish on GitHub&lt;/strong&gt;: The reference implementation; readable and well-organized&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CCRL / TCEC&lt;/strong&gt;: Live ratings and tournament results&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fishtest&lt;/strong&gt;: The testing framework that powers Stockfish development&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AlphaZero's paper&lt;/strong&gt; (Silver et al., 2017): Why neural networks can work without handcrafted evaluation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Happy coding. And may your engine beat your friends.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>algorithms</category>
      <category>cpp</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Why I Built a NAS in 2026: When Your Vendor Abandons You</title>
      <dc:creator>Supreet Sethi</dc:creator>
      <pubDate>Thu, 22 Jan 2026 04:14:31 +0000</pubDate>
      <link>https://dev.to/djinn/why-i-built-a-nas-in-2026-when-your-vendor-abandons-you-4ek8</link>
      <guid>https://dev.to/djinn/why-i-built-a-nas-in-2026-when-your-vendor-abandons-you-4ek8</guid>
      <description>&lt;p&gt;My decade old WD MyCloud still works perfectly. The drives are healthy. The network functions. But the mobile app stopped connecting years ago;Western Digital sunset the cloud services without warning. The hardware outlived the business relationship.&lt;/p&gt;

&lt;p&gt;Meanwhile, my family defaulted to iCloud at $168/year. Forever.&lt;/p&gt;

&lt;p&gt;That's when I understood: &lt;strong&gt;In 2026, your hardware doesn't fail. Your vendor relationships do.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Real Cost of Cloud
&lt;/h2&gt;

&lt;p&gt;When you upload to iCloud or Google Photos, you're not storing photos; you're feeding datasets. Your vacation trains facial recognition. Your kids' birthdays become AI training data. Your entire visual timeline becomes product, not property.&lt;/p&gt;

&lt;p&gt;And you pay $120-168/year for the privilege. In perpetuity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;iCloud Family (2TB): $1,678 over 10 years. Own nothing.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The terms change. Features paywall. Companies pivot. You keep paying or lose access.&lt;/p&gt;

&lt;p&gt;This is the bargain: convenience now, dependency forever.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Build Instead of Buy?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Synology DS423 + drives: $790 AUD&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Beautiful interface, one-click apps, "it just works"&lt;/li&gt;
&lt;li&gt;But: proprietary OS, curated ecosystem, automatic updates you can't control&lt;/li&gt;
&lt;li&gt;When they sunset support in 10 years, you're stuck&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;My Build: $610 AUD&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;N100 mini PC ($370) + 4× 1TB NVMe ($240)&lt;/li&gt;
&lt;li&gt;Ubuntu, ZFS, Immich, PostgreSQL; all open source&lt;/li&gt;
&lt;li&gt;When &lt;em&gt;anything&lt;/em&gt; changes, I adapt. No vendor can abandon me.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc0vujl8fcv8ybzsba6vx.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc0vujl8fcv8ybzsba6vx.jpeg" alt="Homebrew NAS Device" width="800" height="1066"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The difference isn't price. It's sovereignty.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Synology optimizes for simplicity by removing control. I optimized for capability by accepting complexity.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Hardware Choice: NVMe vs HDD
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Why NVMe when HDDs are cheaper?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;4TB NVMe: $240 upfront, $70 power over 10 years = &lt;strong&gt;$310 total&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;4TB HDD: $60 upfront, $520 power over 10 years = &lt;strong&gt;$580 total&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;NVMe saves $270 long-term, runs silent, fits in your palm&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Performance: 1167 MB/s sequential (5× faster than HDDs), 50k+ random IOPS (vs 150 for HDDs). Matters for thumbnails, galleries, facial recognition.&lt;/p&gt;

&lt;p&gt;Trade-off: Can't build massive arrays. But I need 3TB for photos, not 20TB for media servers.&lt;/p&gt;

&lt;p&gt;NVMe isn't splurging; it's optimizing for silence, performance, and efficiency over raw capacity.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Software: Choosing Freedom
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ubuntu 24.04 LTS&lt;/strong&gt;: 6 years of updates, then I choose when to upgrade&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ZFS RAIDZ1&lt;/strong&gt;: Enterprise filesystem, bit rot protection, snapshots; no subscription&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Immich&lt;/strong&gt;: Self-hosted Google Photos with facial recognition, ML search, automatic iPhone backup&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PostgreSQL + Valkey&lt;/strong&gt;: Same stack powering billion-dollar companies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgbd8x5jubhkhcraxjdax.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgbd8x5jubhkhcraxjdax.png" alt="Immich Web Interface" width="800" height="432"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All free. All maintained by communities answering to users, not shareholders.&lt;/p&gt;

&lt;p&gt;No vendor can sunset this. No company can paywall features. No terms can change.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Economics
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;10-Year Total Cost:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;My NAS: $610 + $220 power = &lt;strong&gt;$830&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Synology + power: &lt;strong&gt;$1,310&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;iCloud: &lt;strong&gt;$1,680&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;20-Year Total Cost:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;My NAS: &lt;strong&gt;$1,050&lt;/strong&gt; (just adding electricity)&lt;/li&gt;
&lt;li&gt;Synology: &lt;strong&gt;$2,620&lt;/strong&gt; (power + forced hardware replacement)&lt;/li&gt;
&lt;li&gt;iCloud: &lt;strong&gt;$3,358&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After year 4, my cost drops to $22/year electricity. Cloud costs never decrease. Synology faces eventual forced upgrades when support ends.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But the real economics aren't financial; they're about control.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Cloud companies own your data relationship. Vendors own your software stack. I own everything.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Sovereignty Means
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Photos on hardware I can touch&lt;/li&gt;
&lt;li&gt;No company can raise prices, change terms, or train AI on my family&lt;/li&gt;
&lt;li&gt;When something breaks, I fix it—no support tickets&lt;/li&gt;
&lt;li&gt;Software choices are mine forever&lt;/li&gt;
&lt;li&gt;Zero monthly fees, zero vendor lock-in&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What it costs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;$610 upfront&lt;/li&gt;
&lt;li&gt;One evening of setup&lt;/li&gt;
&lt;li&gt;Willingness to learn&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Who Should Build?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Buy Synology/use iCloud if:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Technology intimidates you&lt;/li&gt;
&lt;li&gt;Time is more valuable than money&lt;/li&gt;
&lt;li&gt;You trust vendors more than yourself&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Build your own if:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Control matters more than convenience&lt;/li&gt;
&lt;li&gt;You're comfortable with Linux basics&lt;/li&gt;
&lt;li&gt;Vendor lock-in bothers you&lt;/li&gt;
&lt;li&gt;You value learning how things work&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Neither is wrong. But only one is sovereign.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why 2026 Is Different
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Hardware:&lt;/strong&gt; $370 quad-core mini PC was impossible five years ago. NVMe prices collapsed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Software:&lt;/strong&gt; Immich rivals Google Photos. ZFS is enterprise-grade. Ubuntu is rock-solid. The tools exist and are free.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cloud costs:&lt;/strong&gt; Rising steadily while adding AI scanning, government requests, and surveillance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Vendor support:&lt;/strong&gt; Companies sunset working products when support becomes unprofitable. My old MyCloud proved this.&lt;/p&gt;

&lt;p&gt;You can now build sovereign infrastructure for less than pre-built solutions, with more capability and zero ongoing dependencies.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Reality Check
&lt;/h2&gt;

&lt;p&gt;This isn't for everyone. If Linux scares you, buy Synology. If you can't spare an evening, pay for iCloud.&lt;/p&gt;

&lt;p&gt;But if you care about privacy, hate subscriptions, value understanding your tools, and worry about vendor abandonment; &lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;3TB usable storage&lt;/strong&gt; (ZFS RAIDZ1, survives one drive failure)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;1167 MB/s writes&lt;/strong&gt; (10× faster than network can use)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Silent operation&lt;/strong&gt; (fanless, 15W total)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic iPhone backup&lt;/strong&gt; with facial recognition and ML search&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Total control&lt;/strong&gt; over every aspect&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Works flawlessly. Family uses it daily. Photos sync automatically. Interface is fast.&lt;/p&gt;

&lt;p&gt;And no vendor can ever turn it off.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Conclusion
&lt;/h2&gt;

&lt;p&gt;In 2026, we've normalized renting access to our own files. Photos don't exist until uploaded to someone's server. Privacy is suspicious. Surveillance is convenient. Working hardware gets abandoned when unprofitable.&lt;/p&gt;

&lt;p&gt;Building a NAS is a small rebellion against this.&lt;/p&gt;

&lt;p&gt;It cost $610 and an evening. It saves $168/year forever. It protects my family's privacy. It can't be sunset.&lt;/p&gt;

&lt;p&gt;My NAS hums at 15 watts, hosting memories that belong to us. Not to Google. Not to Apple. Not to any corporation that might change terms or decide our hardware is too old.&lt;/p&gt;

&lt;p&gt;Just us.&lt;/p&gt;

&lt;p&gt;That's not revolutionary. It's just ownership.&lt;/p&gt;

&lt;p&gt;And in 2026, ownership feels like victory.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Hardware: $610. Monthly fees: $0. Vendor lock-in: none.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Still renting your data, or ready to own it?&lt;/strong&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Digital Transformation: Why 70% Fail; And How 30% Win</title>
      <dc:creator>Supreet Sethi</dc:creator>
      <pubDate>Tue, 09 Dec 2025 05:44:34 +0000</pubDate>
      <link>https://dev.to/djinn/digital-transformation-why-70-fail-and-how-30-win-3lhc</link>
      <guid>https://dev.to/djinn/digital-transformation-why-70-fail-and-how-30-win-3lhc</guid>
      <description>&lt;h2&gt;
  
  
  A Data-Backed Guide to Why 70% Fail and How Winners Get 80% Odds
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Sources:&lt;/strong&gt; BCG (860 executives, 30 case studies), McKinsey (1,793 respondents), Academic literature  &lt;/p&gt;




&lt;h2&gt;
  
  
  THE BRUTAL FACTS
&lt;/h2&gt;

&lt;p&gt;I have personal experience being part of digital journey across India, South East Asia and Australia. It is disturbing to see uncoordinated heap of tactical monikered as digital transformation. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Only 30% of digital transformations succeed&lt;/strong&gt; (meet targets, create sustainable change)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;70% fail or underperform&lt;/strong&gt; (limited value, no durability)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Failed transformations destroy 30–65% of stock value&lt;/strong&gt; (Target: -38% P/E, Sears: bankruptcy)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Successful transformations create 50–100% stock gains&lt;/strong&gt; (Walmart: +62% P/E, Netflix: sustained premium)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The difference? Not technology. Not budget. Six critical success factors.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  THE SIX SUCCESS FACTORS
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Companies that nail ALL SIX achieve &lt;strong&gt;80% success rate&lt;/strong&gt; (vs. 30% baseline)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Factor&lt;/th&gt;
&lt;th&gt;Done Right&lt;/th&gt;
&lt;th&gt;Done Wrong&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;1&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Clear Strategy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Quantified outcomes tied to competitive advantage (Walmart: "omnichannel + retail media margins")&lt;/td&gt;
&lt;td&gt;Vague vision ("go digital"); no competitive advantage articulated (Target)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;2&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Leadership&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;CEO + CFO visible; middle management compensated for transformation; CDO elevated&lt;/td&gt;
&lt;td&gt;CDO role but weak authority; store managers unaligned; no accountability (Target, Sears)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;3&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Talent&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Hire elite external (30%+); free them up full-time; rotation programs (Amazon from Google; Walmart from Amazon)&lt;/td&gt;
&lt;td&gt;Internal reskilling only; underpaid vs. competitors; wrong talent profile (Sears hired store ops people)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;4&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Governance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Weekly steering; decisions &amp;lt;1 week; teams empowered up to $500K (Amazon: "bar raiser" Friday decisions)&lt;/td&gt;
&lt;td&gt;Quarterly reviews; months to resolve blockers; slow approval chains (Target, Sears)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;5&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Monitoring&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Outcome metrics only (growth %, margin bps, deployment speed); real-time dashboards (Walmart: weekly e-comm KPIs)&lt;/td&gt;
&lt;td&gt;Activity metrics ("people trained," "% adoption"); quarterly reviews (Target, GE Digital)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;6&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Architecture&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Microservices, API-first, real-time data pipelines; deploy weekly (Netflix, Amazon: 1000+ deploys/day)&lt;/td&gt;
&lt;td&gt;Monolithic systems, bolted-on integrations, 6-8 week cycles (Target, Sears mainframe)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  WINNERS: WHAT THEY DID RIGHT
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Walmart (+62.3% P/E, 2020–2025)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;The Transformation:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Discount grocer → omnichannel powerhouse with recurring revenue (retail media)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Execution:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Strategy:&lt;/strong&gt; "E-commerce to 25%+ revenue; retail media to $5B; margin expansion"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Leadership:&lt;/strong&gt; CEO Doug McMillon visible; middle managers get 20-30% bonus for e-commerce growth&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Talent:&lt;/strong&gt; Hired CTOs from Amazon/Google; 18-24 month rotation program at 30% premium salary&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Governance:&lt;/strong&gt; Weekly steering committee; blockers resolved in &amp;lt;7 days&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitoring:&lt;/strong&gt; Published metrics (26% e-commerce growth, 93% same-day reach, $4.4B retail media)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Architecture:&lt;/strong&gt; Microservices, API-first, real-time data ingestion&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; Operating margin +28 bps; retail media 70% margins; P/E 21.69 → 35.20&lt;/p&gt;




&lt;h3&gt;
  
  
  Amazon (1995–Present, Continuous Pivot)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;The Transformation:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Bookstore → e-commerce → cloud infrastructure → AI-first platform&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Execution:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Strategy:&lt;/strong&gt; Customer obsession; tied to measurable metrics (speed, cost, defect rate)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Leadership:&lt;/strong&gt; "Day 1" culture; removed leaders who couldn't shift to growth mindset&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Talent:&lt;/strong&gt; "Hire the best engineer you know"; 40%+ from external&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Governance:&lt;/strong&gt; Weekly decisions; two-pizza rule (small teams, autonomous decisions)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitoring:&lt;/strong&gt; Real-time customer metrics drive product roadmap&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Architecture:&lt;/strong&gt; Service-oriented from day 1; 1000+ deployments/day&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; 5000%+ stock return since IPO; innovation velocity 3x industry average&lt;/p&gt;




&lt;h3&gt;
  
  
  Netflix (2007–2025, Multi-Pivot)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;The Transformation:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Physical rental → streaming → original content&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Execution:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Strategy:&lt;/strong&gt; Each pivot with clear metrics (subscribers, churn, content ROI)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Leadership:&lt;/strong&gt; "Context over control"; failed experiments celebrated&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Talent:&lt;/strong&gt; High-performer culture; recruited from startups and tech giants&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Governance:&lt;/strong&gt; Weekly steering; innovation lab runs failed experiments in weeks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitoring:&lt;/strong&gt; Real-time subscriber/engagement dashboards drive spend allocation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Architecture:&lt;/strong&gt; Cloud-native (AWS); real-time data pipeline&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; 300M+ subscribers; P/E premium 60–80x sustained; market dominance&lt;/p&gt;




&lt;h3&gt;
  
  
  Microsoft (2014–2025, Cloud Pivot Under Satya Nadella)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;The Transformation:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Windows/licensing → cloud-first + AI strategy&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Execution:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Strategy:&lt;/strong&gt; "Mobile-first, cloud-first" (contradicted legacy Windows strategy)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Leadership:&lt;/strong&gt; Nadella removed executives who resisted growth mindset&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Talent:&lt;/strong&gt; Hired Chief Cloud Officer from Amazon, CIO from Google&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Governance:&lt;/strong&gt; Quarterly (faster than pre-Nadella annual cycles)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitoring:&lt;/strong&gt; Azure revenue growth, churn, feature time-to-market tied to exec comp&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Architecture:&lt;/strong&gt; Cloud-native; microservices; API-first&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; Azure $60B+ business (25–35% YoY growth); stock 20x return; P/E +60%&lt;/p&gt;




&lt;h2&gt;
  
  
  LOSERS: WHAT THEY GOT WRONG
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Target (–38.2% P/E, 2019–2025)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Why It Failed:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Strategy:&lt;/strong&gt; "Become digital retailer" (vague) with no competitive advantage articulated&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Leadership:&lt;/strong&gt; CDO present but not empowered and  store managers unsupported&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Talent:&lt;/strong&gt; &amp;lt;30% external hiring; 8-week training programs resulting in weak execution in comparison to competitors&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Governance:&lt;/strong&gt; Quarterly reviews - 4+ quarter blockers and quality of delivered E-commerce experiential inferior to Walmart&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitoring:&lt;/strong&gt; Lack of clear activity metrics ("# trained," "% adoption") and no clear measure of gaps&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Architecture:&lt;/strong&gt; Bolted omnichannel onto monolith with 6–8 week deployment cycles&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; Billions spent, market share lost to Walmart, valuation compressed 38%&lt;/p&gt;




&lt;h3&gt;
  
  
  GE Digital ($5B+ sunk cost, then divested)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Why It Failed:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Strategy:&lt;/strong&gt; Predix platform separate from core business (not integrated into manufacturing divisions)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Leadership:&lt;/strong&gt; Manufacturing divs had no accountability for adoption&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Talent:&lt;/strong&gt; Reskilled mechanical engineers as software engineers (cultural mismatch)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Governance:&lt;/strong&gt; Annual strategic reviews (too slow for SaaS)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitoring:&lt;/strong&gt; Vanity metrics (customer logos, not product-market fit)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Architecture:&lt;/strong&gt; Optimized for GE's use case; not scalable to market&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; $5B investment → zero market traction → divested&lt;/p&gt;




&lt;h3&gt;
  
  
  Sears &amp;amp; Blockbuster (→ Bankruptcy, 99%+ value destruction)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Why They Failed:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Strategy:&lt;/strong&gt; Denial ("physical will always dominate")&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Leadership:&lt;/strong&gt; CEO focused on cost-cutting, not strategic positioning&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Talent:&lt;/strong&gt; No hiring; promoted store ops people to digital leadership&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Governance:&lt;/strong&gt; Quarterly decisions; market moved faster&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitoring:&lt;/strong&gt; No competitive positioning metrics&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Architecture:&lt;/strong&gt; Mainframe-era systems; $500M+ rearchitecture needed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; Stock $100+ (2000s) → bankruptcy (2010–2018)&lt;/p&gt;




&lt;h2&gt;
  
  
  THE SCORECARD: 6 FACTORS ACROSS 8 COMPANIES
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Company&lt;/th&gt;
&lt;th&gt;Strategy&lt;/th&gt;
&lt;th&gt;Leadership&lt;/th&gt;
&lt;th&gt;Talent&lt;/th&gt;
&lt;th&gt;Governance&lt;/th&gt;
&lt;th&gt;Monitoring&lt;/th&gt;
&lt;th&gt;Architecture&lt;/th&gt;
&lt;th&gt;Score&lt;/th&gt;
&lt;th&gt;Outcome&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Walmart&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;6/6&lt;/td&gt;
&lt;td&gt;+62% P/E&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Amazon&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;6/6&lt;/td&gt;
&lt;td&gt;+5000%+ stock&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Netflix&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;6/6&lt;/td&gt;
&lt;td&gt;Premium maintained&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Microsoft&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;6/6&lt;/td&gt;
&lt;td&gt;+60% P/E, 20x stock&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Target&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;0–2/6&lt;/td&gt;
&lt;td&gt;–38% P/E&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GE Digital&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;0–1/6&lt;/td&gt;
&lt;td&gt;Divested, $5B+ loss&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sears&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;0–1/6&lt;/td&gt;
&lt;td&gt;Bankruptcy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Blockbuster&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;0/6&lt;/td&gt;
&lt;td&gt;Bankruptcy&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  THE MATH THAT MATTERS
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Success Probability by Score:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;6/6 factors:&lt;/strong&gt; 80% success → 50–100% P/E expansion&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;4–5/6 factors:&lt;/strong&gt; 40–50% success → flat to slight premium&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2–3/6 factors:&lt;/strong&gt; 20–30% success → valuation stall&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;0–1/6 factors:&lt;/strong&gt; 5–10% success → 30–65% compression or bankruptcy&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Market Impact:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Winner (Walmart) P/E: 35.20 → &lt;strong&gt;35% premium over sector median&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Loser (Target) P/E: 11.13 → &lt;strong&gt;30% discount to peer average&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Gap: 3x multiple difference → same industry, same decade&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  THE FORMULA (ULTRA-CRISP)
&lt;/h2&gt;

&lt;p&gt;Digital transformation succeeds if and only if &lt;strong&gt;ALL SIX are in place:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Clear, quantified strategy&lt;/strong&gt; (not "go digital"; say "hit 25% e-commerce, add $5B margin-rich revenue")&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CEO-led commitment&lt;/strong&gt; (visible, not delegated; middle management compensated)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Right talent&lt;/strong&gt; (hire elite external; free them up full-time; pay premium)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fast governance&lt;/strong&gt; (weekly decisions; &amp;lt;7 day escalation; teams empowered)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Outcome metrics only&lt;/strong&gt; (growth %, margin improvement, speed); publish weekly&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Modern architecture&lt;/strong&gt; (microservices, API-first, real-time data; 1–2 week deployments)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Miss one:&lt;/strong&gt; risk spikes to 50–70%.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Miss three:&lt;/strong&gt; failure almost certain; valuation collapse imminent.&lt;/p&gt;




&lt;h2&gt;
  
  
  FINAL TAKEAWAY
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;There is no luck in digital transformation.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Winners (Walmart, Amazon, Netflix, Microsoft) got all six factors right and compound their advantage yearly. Losers (Target, GE Digital, Sears, Blockbuster) skipped one or more factors and paid the price—sometimes bankruptcy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your job: Get all six right. Not approximately. All six.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The market knows the difference. Your stock price will reflect it in 2–3 years.&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>leadership</category>
      <category>management</category>
    </item>
    <item>
      <title>Don't Touch Your Trade: The Most Underrated Strategy in Investing</title>
      <dc:creator>Supreet Sethi</dc:creator>
      <pubDate>Mon, 08 Dec 2025 02:21:48 +0000</pubDate>
      <link>https://dev.to/djinn/dont-touch-your-trade-the-most-underrated-strategy-in-investing-1f7f</link>
      <guid>https://dev.to/djinn/dont-touch-your-trade-the-most-underrated-strategy-in-investing-1f7f</guid>
      <description>&lt;p&gt;You've been staring at your portfolio for 15 minutes. A stock is down 3%. Should you rebalance? Check another screener? Buy the dip? Optimize something?&lt;/p&gt;

&lt;p&gt;Stop.&lt;/p&gt;

&lt;p&gt;That itch to tinker—to debug, iterate, optimize—is making you poorer.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Uncomfortable Truth: Activity Is the Enemy
&lt;/h2&gt;

&lt;p&gt;Here's what the data says:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Retail investors underperform their own funds by 1–2% annually.&lt;/strong&gt; Not because they pick bad stocks. Not because of fees. Because they trade too much.&lt;/p&gt;

&lt;p&gt;Day traders? Worse. They underperform by 5–10% annually, even accounting for survivorship bias.&lt;/p&gt;

&lt;p&gt;Morningstar's "Mind the Gap" study tracked this for two decades. Dalbar's research confirmed it. Academic studies on retail flows all point to the same culprit:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;More trades → Emotional decisions → Worse timing → Compounding in reverse.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The math is brutal. A 15–30% wealth gap over a decade. That's not negligible. That's retirement-level money.&lt;/p&gt;

&lt;p&gt;And here's the thing: it's not stupidity. It's busyness.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Your Developer Brain Backfires
&lt;/h2&gt;

&lt;p&gt;You're trained to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Increase iterations&lt;/li&gt;
&lt;li&gt;Reduce latency
&lt;/li&gt;
&lt;li&gt;Debug fast&lt;/li&gt;
&lt;li&gt;Ship incremental improvements constantly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This works beautifully in code. It's a disaster in markets.&lt;/p&gt;

&lt;p&gt;Markets aren't deterministic. They're stochastic, adversarial, and soaked in noise. The signal-to-noise ratio is terrible. Which means your optimization instinct—your superpower in engineering—becomes a liability.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The rule flips:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code: "Touch the system frequently"&lt;/li&gt;
&lt;li&gt;Markets: "Touch the system rarely"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every decision point is a chance to mess up. Every trade, a potential emotional mistake. The best traders aren't the busiest—they're the most selective.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Actual Edge: Do Nothing
&lt;/h2&gt;

&lt;p&gt;If you study every winning strategy that's beaten the S&amp;amp;P 500 over 20+ years, they all collapse to the same principle:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Buy quality. Hold. Interfere minimally.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When you stop trading:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Compounding compounds (not interrupted by poor timing)&lt;/li&gt;
&lt;li&gt;Fees evaporate (fewer transactions = lower costs)&lt;/li&gt;
&lt;li&gt;Taxes shrink (long-term capital gains &amp;gt; short-term realized losses)&lt;/li&gt;
&lt;li&gt;Emotional mistakes disappear (can't make bad trades if you don't trade)&lt;/li&gt;
&lt;li&gt;Execution becomes simple (fewer decision points = fewer ways to fail)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Think of it like background processes. Compounding is a thread running 24/7 whether you're awake or asleep. Your job isn't to optimize it. It's to not kill it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Events vs. Signals: The Mental Model That Changes Everything
&lt;/h2&gt;

&lt;p&gt;Most traders confuse noise for information:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Events (ignore these):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A tweet&lt;/li&gt;
&lt;li&gt;A Fed headline&lt;/li&gt;
&lt;li&gt;A stock pop or drop&lt;/li&gt;
&lt;li&gt;Earnings beat by 2%&lt;/li&gt;
&lt;li&gt;An analyst upgrade&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Signals (trade on these):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Earnings &lt;em&gt;trends&lt;/em&gt; (last 4 quarters improving)&lt;/li&gt;
&lt;li&gt;Free cash flow inflection&lt;/li&gt;
&lt;li&gt;Long-term growth acceleration&lt;/li&gt;
&lt;li&gt;Margin expansion (structural, not cyclical)&lt;/li&gt;
&lt;li&gt;Industry disruption with clear winners&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Events are fast. Signals are slow. If your portfolio is changing weekly, you're trading on events. And you're losing.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Rule You Can Adopt Monday Morning
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The Weekly Rule:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Review portfolio: once per week&lt;/li&gt;
&lt;li&gt;Make decisions: once per month&lt;/li&gt;
&lt;li&gt;Trade: only when something fundamental shifts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The Quarterly Rule (for long-term folks):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Thesis broken? Trade&lt;/li&gt;
&lt;li&gt;Earnings trajectory changed? Consider it&lt;/li&gt;
&lt;li&gt;Price moved? Ignore it completely&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's it. Most investors would triple their returns with this discipline alone.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Code This Into Your System
&lt;/h2&gt;

&lt;p&gt;If you were automating "trade less, earn more," it'd look like:&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;should_trade&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;signal_strength&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;thesis_intact&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;thesis_intact&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;signal_strength&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;0.85&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;  &lt;span class="c1"&gt;# Default: NO TRADE
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See that final return? In your codebase, no-op is the most profitable branch in the long run.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Payoff: Better Returns, Better Sleep
&lt;/h2&gt;

&lt;p&gt;When you trade less:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You stop chasing momentum (the #1 killer of returns)&lt;/li&gt;
&lt;li&gt;FOMO-driven buys vanish&lt;/li&gt;
&lt;li&gt;Revenge trades disappear&lt;/li&gt;
&lt;li&gt;You sleep eight hours instead of checking futures&lt;/li&gt;
&lt;li&gt;Your actual returns start matching market returns&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ironically, trading less is how you stop underperforming and finally start outperforming.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Idempotence Principle
&lt;/h2&gt;

&lt;p&gt;A robust system doesn't break when you run the same command twice. It doesn't require constant intervention. It's stable and predictable.&lt;/p&gt;

&lt;p&gt;Your portfolio should be the same.&lt;/p&gt;

&lt;p&gt;If your strategy forces 20 decisions per week, it's not sophisticated—it's brittle. The best portfolios are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Predictable&lt;/li&gt;
&lt;li&gt;Repeatable&lt;/li&gt;
&lt;li&gt;Boring&lt;/li&gt;
&lt;li&gt;Idempotent&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And boring portfolios are profitable portfolios.&lt;/p&gt;

&lt;h2&gt;
  
  
  The One Edge You Actually Need
&lt;/h2&gt;

&lt;p&gt;You don't need to beat hedge funds. You don't need perfect timing. You don't need machine learning or a proprietary screener.&lt;/p&gt;

&lt;p&gt;You need one thing:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The discipline to do nothing most of the time.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That's not laziness. That's leverage.&lt;/p&gt;

&lt;p&gt;Trade less. Earn more. Sleep better.&lt;/p&gt;

</description>
      <category>stocks</category>
      <category>python</category>
      <category>developer</category>
      <category>investments</category>
    </item>
    <item>
      <title>I Hired AWS SAs for 5 Years. Now That I've Left, I Can Tell You the Truth</title>
      <dc:creator>Supreet Sethi</dc:creator>
      <pubDate>Thu, 27 Nov 2025 21:41:45 +0000</pubDate>
      <link>https://dev.to/djinn/i-hired-aws-sas-for-5-years-now-that-ive-left-i-can-tell-you-the-truth-138o</link>
      <guid>https://dev.to/djinn/i-hired-aws-sas-for-5-years-now-that-ive-left-i-can-tell-you-the-truth-138o</guid>
      <description>&lt;p&gt;&lt;strong&gt;tl;dr&lt;/strong&gt;: I spent five years interviewing SA candidates at AWS. Comprehensive AWS training doesn't correlate with hire decisions. Production talk and clear thinking do. Now that I'm external, I can say it plainly: most interview prep is wasted effort.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Uncomfortable Truth
&lt;/h2&gt;

&lt;p&gt;I was a Principal SA at AWS for two years. I sat on hiring panels as a Manager after that for three years. I made hire/no-hire decisions. I watched hundreds of candidates prepare the wrong way and then blame themselves for not studying hard enough.&lt;/p&gt;

&lt;p&gt;Now that I've left, I can say this plainly without corporate-speak: &lt;strong&gt;your comprehensive AWS study guide is doing more harm than good.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We don't care if you memorized the Well-Architected Framework. We didn't care if you studied all 200 AWS services. We cared if you could think clearly about tradeoffs, defend a decision under pressure, and admit when you didn't know something.&lt;/p&gt;

&lt;p&gt;Yet every study guide, bootcamp, and LinkedIn thread screams the same lie: &lt;strong&gt;you need comprehensive AWS training&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You don't. In fact, it often works against you.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Happens in the Room
&lt;/h2&gt;

&lt;p&gt;Your interviewer—especially if they're a Principal or staff engineer—already knows you haven't deployed infrastructure across 47 different AWS services. They're not testing breadth. They're testing &lt;em&gt;depth of thinking&lt;/em&gt; and &lt;em&gt;production judgment&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Here's what I paid attention to when candidates walked in:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Production context—where's the real decision?&lt;/strong&gt;&lt;br&gt;
"I've built with EC2 and RDS" is fine. But I listened for &lt;em&gt;why&lt;/em&gt; you made choices. Instance families? Why? Scaling problems? Walk me through one. Connection pooling in production? Perfect—now we're talking about real engineering.&lt;/p&gt;

&lt;p&gt;The candidates I voted to hire almost always had one or two services they knew cold because they shipped something real. The ones reading from study guides tried to sound like they knew everything, and it was immediately obvious.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Can you think on your feet?&lt;/strong&gt;&lt;br&gt;
I watched how people handled "how would you architect X?" If they recited a framework answer, I'd already mentally moved on to the next candidate. If they asked me clarifying questions, identified genuine tradeoffs, and said "I'd lean toward Fargate here because—," I'd lean forward.&lt;/p&gt;

&lt;p&gt;The best moment in an interview is when a candidate says "I haven't used that, but here's my hypothesis based on..." That's the real move. That's how actual architects work. They don't know everything. They think.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Honest gaps are revealing&lt;/strong&gt;&lt;br&gt;
I built my hiring questions specifically to find the difference between "studied this last week" and "this is my blind spot." When someone said, "I'd need to research that, but here's my starting point," they immediately became more credible than someone bulldozing with buzzwords.&lt;/p&gt;

&lt;p&gt;The worst interviews? Candidates stalling on basic questions while clearly trying to remember what slide 47 of their bootcamp said.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Training Industrial Complex (Or: Why Your $500 Course Hurt Your Chances)
&lt;/h2&gt;

&lt;p&gt;Five years on the hiring side has shown me a clear pattern: candidates who take comprehensive AWS training courses often &lt;em&gt;perform worse&lt;/em&gt; than those who don't.&lt;/p&gt;

&lt;p&gt;It's counterintuitive, but the data's there:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Candidate studies 200 services -&amp;gt; interviews with surface-level knowledge of many, depth in none&lt;/li&gt;
&lt;li&gt;Candidate ships one project with EC2/RDS -&amp;gt; interviews with real architectural thinking&lt;/li&gt;
&lt;li&gt;Interview panel immediately spots rehearsed answers and deprioritizes&lt;/li&gt;
&lt;li&gt;Candidate blames themselves: "I should have studied the ECS section more"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Wrong diagnosis. Your study guide didn't help you; it convinced you that breadth matters more than depth.&lt;/p&gt;

&lt;p&gt;The cruel cycle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Candidate feels anxious &amp;gt; buys $500 "Complete AWS for SAs" course&lt;/li&gt;
&lt;li&gt;Course teaches 200 services at surface level&lt;/li&gt;
&lt;li&gt;Candidate memorizes facts they'll forget in 2 weeks&lt;/li&gt;
&lt;li&gt;Interview panel spots the rehearsed answers within 10 minutes&lt;/li&gt;
&lt;li&gt;Candidate doesn't get the job&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;They blame themselves for not studying harder&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I've sat through hundreds of interviews. The training industry profits off this anxiety loop while genuinely hurting candidate performance.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You Actually Need
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;For the phone screen:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Production experience with at least one primary service (compute, database, or whatever)&lt;/li&gt;
&lt;li&gt;Ability to discuss a real project: what worked, what didn't, what you'd change&lt;/li&gt;
&lt;li&gt;Willingness to think through new problems without pretending you have all the answers&lt;/li&gt;
&lt;li&gt;AWS fundamentals (VPC, IAM, Security Groups, GW, basic networking)—these are legitimate&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;For the technical discussion:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deep knowledge of services &lt;em&gt;you've used&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Framework for thinking about tradeoffs (cost vs. complexity, availability vs. simplicity, etc.)&lt;/li&gt;
&lt;li&gt;Ability to pivot and explore adjacent solutions&lt;/li&gt;
&lt;li&gt;Comfortable saying "I'd need to research that, but here's my starting hypothesis"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's genuinely it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Real Test: A Rugby Social Network Migration
&lt;/h2&gt;

&lt;p&gt;Let me walk you through an actual scenario AWS uses in interviews—and what separates the good architects from the ones who fail despite being technically sound.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The problem:&lt;/strong&gt; A rugby-focused social network with 2M users, growing 5x in the next 12 months. Currently on-premises in Dallas. Budget: $25K/month (15K hosting, 10K CDN via Akamai). Users spread: US 30%, Europe 50%, Asia 20%. They need to move to AWS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bad SA answer&lt;/strong&gt; (technically competent, but fails):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"We'd use EC2 for compute, RDS for the database, S3 for video storage, CloudFront for CDN to replace Akamai, ElastiCache for sessions, Route53 for DNS, Auto Scaling groups, VPC with public/private subnets across three AZs, IAM roles for service-to-service auth, CloudWatch for monitoring, potentially Lambda for event processing, maybe Kinesis for real-time notifications, SQS for async jobs..."&lt;/li&gt;
&lt;li&gt;Goes deeper: task definitions, RTO/RPO, security groups, NACLs, database backup strategies&lt;/li&gt;
&lt;li&gt;Panel internally: "They know AWS components. But I have no idea if they can actually make a decision."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rejected&lt;/strong&gt; (despite being technically correct)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Good SA answer&lt;/strong&gt; (the one I hire):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Three regions: us-east-1 for US traffic, eu-west-1 for Europe, ap-southeast-1 for Asia. Each region gets an application tier and a database. We'll start with EC2 for the app layer—I'd use t3.large with ASGs to handle the 5x growth. RDS Multi-AZ in each region for data durability. We're replacing Akamai with CloudFront, which saves 10K/month immediately and actually improves latency in Europe and Asia."&lt;/li&gt;
&lt;li&gt;Pauses. Asks clarifying questions: "Are writes distributed or mostly from content creators? How real-time are notifications? Is the data read-heavy or write-heavy?"&lt;/li&gt;
&lt;li&gt;Listens to answer, then adapts: "If it's 80/20 read/write, we could add ElastiCache in front of RDS to reduce database load during peak traffic. If the user base is mostly mobile, we might want to think about API Gateway + Lambda instead of EC2 to reduce operational overhead—but EC2 gives us more predictable scaling for a 5x ramp."&lt;/li&gt;
&lt;li&gt;When asked about databases: "RDS is safe here because we're relational. DynamoDB would require a schema redesign we don't have time for during a 5x growth. We validate that choice once we see real query patterns."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hired&lt;/strong&gt; (someone who thinks, not someone who lists)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The difference? The good SA made &lt;em&gt;three architectural decisions&lt;/em&gt; (regional distribution, EC2 over serverless, RDS over NoSQL) with &lt;em&gt;clear reasoning&lt;/em&gt; based on &lt;em&gt;constraints&lt;/em&gt;. The bad SA listed every service and hoped some combination was right.&lt;/p&gt;

&lt;p&gt;Then I tweaked the problem: "What if we need to migrate 500TB of video content and we can't afford 6 months of dual-running on-prem + cloud?"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Good SA pivots in real time:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"We'd use AWS DataSync to replicate data continuously for the first month while we're in parallel. Once the on-prem systems reach end of life, we flip the DNS. But more importantly, we don't need S3 for video storage if we're staying with Akamai for CDN—Akamai has edge storage. So we replicate video to Akamai's origin once, we store backups in S3, but the application doesn't serve from S3. That keeps our architecture simple and maintains the budget."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Still hired&lt;/strong&gt; (adapted correctly)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Bad SA in same scenario:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Panics slightly. Falls back to memorized migration strategies.&lt;/li&gt;
&lt;li&gt;Starts listing: "We could use AWS DataSync, AWS Direct Connect, AWS Snowball..."&lt;/li&gt;
&lt;li&gt;Doesn't actually &lt;em&gt;choose&lt;/em&gt; or &lt;em&gt;reason about cost and timeline&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Still rejected&lt;/strong&gt; (couldn't think under pressure)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why "Describe Every Component" Actually Kills Your Interview
&lt;/h2&gt;

&lt;p&gt;When you try to faithfully describe every possible AWS component in your architecture, you're signaling something deadly to the panel: &lt;strong&gt;you're not making decisions. You're reciting a design.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here's what happens in the room:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The candidate who maps components:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lists RDS, EC2, S3, CloudFront, ElastiCache, Route53, IAM, VPC, subnets, security groups, NACLs, KMS, CloudWatch, CloudTrail...&lt;/li&gt;
&lt;li&gt;For each one, provides correct technical details&lt;/li&gt;
&lt;li&gt;Panel thinks: "They know AWS. But I don't know if they'd actually make this decision if costs were cut in half. Do they know why each component is there, or are they just pattern-matching to 'enterprise architecture template #42'?"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The candidate who makes three decisions with reasoning:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Multi-region for latency (us-east, eu-west, ap-southeast). EC2+RDS+CloudFront at each region. No serverless yet—5x growth means we need predictable scaling."&lt;/li&gt;
&lt;li&gt;For each decision, brief reasoning: cost, operational burden, time-to-migrate&lt;/li&gt;
&lt;li&gt;When challenged ("What about DynamoDB?"), they know &lt;em&gt;why&lt;/em&gt; they chose RDS instead&lt;/li&gt;
&lt;li&gt;Panel thinks: "If requirements change, this person will adapt the design, not rebuild from a different AWS architecture template."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The second person will design better systems under real pressure because they're reasoning about tradeoffs, not memorizing patterns.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The interview is testing your ability to:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Make decisions under constraint (time, budget, team skills)&lt;/li&gt;
&lt;li&gt;Explain decisions in 30 seconds (broad strokes)&lt;/li&gt;
&lt;li&gt;Change your mind when new information arrives (during the tweaks)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It's &lt;em&gt;not&lt;/em&gt; testing whether you know all 200+ AWS services exist.&lt;/p&gt;

&lt;p&gt;The cloud industry's obsession with breadth-first interview preparation isn't about better hiring. It's about justifying expensive training platforms while maintaining the illusion that AWS is harder than it actually is.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS is not hard. Thinking clearly about tradeoffs is hard.&lt;/strong&gt; And no course teaches that. Only production teaches that.&lt;/p&gt;

&lt;p&gt;Now that I'm building hiring processes at a new company, I'm explicitly deprioritizing "studied the most material" in favor of "shipped the most thoughtful decisions." We ask fewer "how many EC2 instance types exist?" questions and more "walk me through a bad architectural decision you made and how you'd change it." That's actually predictive. That's the kind of candidate who will design better systems under real pressure.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Actually Prepare (The Right Way)
&lt;/h2&gt;

&lt;p&gt;Stop thinking in components. Start thinking in decisions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pick a real migration or scaling problem.&lt;/strong&gt; Use the rugby case as a template:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;2M users growing 5x in 12 months&lt;/li&gt;
&lt;li&gt;Geographic distribution (not just US)&lt;/li&gt;
&lt;li&gt;Budget constraint ($25K/month)&lt;/li&gt;
&lt;li&gt;Existing infrastructure you're replacing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;For your practice problem, write down:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Three architectural decisions.&lt;/strong&gt; Not "use EC2, RDS, and CloudFront." Write: "Multi-region distribution because 50% of traffic is Europe and latency matters. EC2 instead of Fargate because we need cost predictability during 5x growth and ops team is Linux-comfortable. RDS instead of DynamoDB because schema is relational and migration timeline is tight."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why each decision, in one sentence.&lt;/strong&gt; If you need three sentences to justify it, you don't understand it well enough.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What you'd change if...&lt;/strong&gt; Cost cut in half? Timeline compressed to 6 weeks? User base only in US? Changing any constraint should trigger a design change. If your design doesn't flex, you don't own it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;In the interview, lead with the broad strokes:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"I'd go multi-region, EC2+RDS in each region, CloudFront for CDN, and here's why..."&lt;/li&gt;
&lt;li&gt;Don't list components. List decisions.&lt;/li&gt;
&lt;li&gt;When they ask about a component you didn't mention ("What about caching?"), say: "Good point—I'd add ElastiCache if query patterns show we need it, but we validate that in week two post-migration. Right now, I'm optimizing for certainty over premature optimization."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Be ready for the tweaks:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"What if migration takes 2 weeks, not 2 months?" → "We'd use DataSync and parallel run, but that changes our budget. What's the hard constraint?"&lt;/li&gt;
&lt;li&gt;"What if the team is Node.js only, not infrastructure-familiar?" → "Serverless becomes more attractive. Lambda+RDS+API Gateway probably safer than managing ASGs."&lt;/li&gt;
&lt;li&gt;"What if they won't pay for multi-region?" → "Single region initially, but we architect for multi-region addition—CloudFront origin failover, RDS read replicas we can promote. Route 53 health checks point to healthy region."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is how real architects work. You start with broad strokes, you defend those strokes, and you pivot when constraints change.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;If you're preparing for an AWS SA role and thinking "I'm not ready because I haven't studied enough," stop. You're probably overthinking this.&lt;/p&gt;

&lt;p&gt;You're ready if you can talk about real production problems and think through new ones. That's the job.&lt;/p&gt;

&lt;p&gt;Stop cramming. Start shipping. The interview will feel different when you have stories instead of slides.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Curious if your production experience translates?&lt;/strong&gt; Talk about a real architectural decision you made, no matter how small. That's your interview prep.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>The Melbourne Talent War: A Conversation</title>
      <dc:creator>Supreet Sethi</dc:creator>
      <pubDate>Thu, 27 Nov 2025 06:45:50 +0000</pubDate>
      <link>https://dev.to/djinn/the-melbourne-talent-war-a-conversation-kac</link>
      <guid>https://dev.to/djinn/the-melbourne-talent-war-a-conversation-kac</guid>
      <description>&lt;p&gt;&lt;strong&gt;Setting:&lt;/strong&gt; Fitzroy café. Afternoon sun. Coffee that tastes like punishment.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;JAMES:&lt;/strong&gt; Supreet, hiring in Melbourne is a nightmare. Everyone wants “top engineers,” no one can find them. And when they do, the person quits in six months for a 20k bump. I’ve never seen the market this thin.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SUPREET:&lt;/strong&gt; Yeah, I’m hearing the same from clients. And honestly—this isn’t new to me. I lived a version of this in Indonesia back in 2015.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JAMES:&lt;/strong&gt; What happened there?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SUPREET:&lt;/strong&gt; We were trying to build a cloud team for a client. AWS was taking off, but Jakarta didn’t have many cloud engineers. Maybe ten people in the whole market who had touched EC2. Singapore companies were paying double. Local companies couldn’t compete.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JAMES:&lt;/strong&gt; Sounds familiar already.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SUPREET:&lt;/strong&gt; Exactly. So instead of chasing unicorns, we changed the game. We stopped trying to hire “cloud engineers.” Because they barely existed. We hired for &lt;em&gt;raw talent&lt;/em&gt;. Smart people. Curious people. Didn’t matter if they were from helpdesk, QA, support, or networking.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JAMES:&lt;/strong&gt; So you made cloud engineers instead of finding them?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SUPREET:&lt;/strong&gt; Pretty much. We trained them from scratch. Linux basics. Network fundamentals. How cloud pricing worked. How to think about architecture. I mentored them directly, pushed them on real client problems, threw them into high-pressure situations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JAMES:&lt;/strong&gt; That sounds… intense.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SUPREET:&lt;/strong&gt; It had to be. But in a year, they were better than people with “five years cloud” on the résumé. Because they learned deeply, not shallowly.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;JAMES:&lt;/strong&gt; Melbourne would lose its mind over that. Everyone wants “experience with Kubernetes” or “five years React,” whatever. No one wants to train.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SUPREET:&lt;/strong&gt; Right. And yet everyone complains they can’t find talent. That’s the contradiction. You can’t have both.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JAMES:&lt;/strong&gt; How did you even find these people in Indonesia? If you weren’t filtering for cloud experience, what were you looking for?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SUPREET:&lt;/strong&gt; Signals. Stuff no job board filters for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Could they think clearly?&lt;/li&gt;
&lt;li&gt;Did they take initiative in past roles?&lt;/li&gt;
&lt;li&gt;Were they hungry to learn?&lt;/li&gt;
&lt;li&gt;Did their résumé show actual &lt;em&gt;effort&lt;/em&gt;, not just job titles?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some of the best were people who tinkered with Linux at home or fixed their family’s WiFi for fun.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JAMES:&lt;/strong&gt; &lt;em&gt;(laughs)&lt;/em&gt; Classic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SUPREET:&lt;/strong&gt; But that curiosity was gold. Teachable. Reliable. Sustainable.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;JAMES:&lt;/strong&gt; And they stayed?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SUPREET:&lt;/strong&gt; Most did. Because the mentorship was the real glue. They weren’t just employees—they were being built. And they knew it. They knew they were growing faster than anywhere else.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JAMES:&lt;/strong&gt; So mentoring was the retention strategy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SUPREET:&lt;/strong&gt; It was the whole strategy. People stay where they grow. Not where they get the highest salary.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JAMES:&lt;/strong&gt; But some still left, right?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SUPREET:&lt;/strong&gt; Of course. Once they got good, Singapore recruiters came calling. Two left. Hurt a little. But the rest stayed because the growth, culture, and trust mattered more than a few extra thousand dollars.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;JAMES:&lt;/strong&gt; Okay, bring this back to Melbourne. How does this translate?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SUPREET:&lt;/strong&gt; Melbourne right now is what Jakarta was in 2015.&lt;br&gt;
Cloud, platform, DevOps—demand is massive, supply is tiny.&lt;/p&gt;

&lt;p&gt;Companies keep trying to “buy” senior talent. It won’t work.&lt;/p&gt;

&lt;p&gt;The only consistent strategy is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hire for potential. Mentor hard. Build the team you need.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JAMES:&lt;/strong&gt; And what does “hire for potential” look like here?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SUPREET:&lt;/strong&gt; Forget the keyword checklist. Look for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;strong fundamentals (networking, Linux, systems thinking)&lt;/li&gt;
&lt;li&gt;curiosity&lt;/li&gt;
&lt;li&gt;self-learners&lt;/li&gt;
&lt;li&gt;people who’ve built anything on their own&lt;/li&gt;
&lt;li&gt;people who write clearly, think clearly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Someone who built a home lab is more valuable than someone who wrote “AWS certified” but never designed anything real.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;JAMES:&lt;/strong&gt; And the mentoring? How do you structure that?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SUPREET:&lt;/strong&gt; Weekly 1:1s. Real feedback. Pairing on real issues. Give them responsibility early. Don’t treat juniors like juniors. Treat them like apprentices.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JAMES:&lt;/strong&gt; That’s… not common here.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SUPREET:&lt;/strong&gt; Exactly why it works.&lt;br&gt;
It’s rare.&lt;br&gt;
It creates loyalty.&lt;br&gt;
And it solves the hiring problem permanently.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;JAMES:&lt;/strong&gt; So your message to Melbourne companies is basically:&lt;br&gt;
“Stop complaining about the senior talent shortage. Build it.”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SUPREET:&lt;/strong&gt; Yep. Because the truth is, the people who will become your best engineers are currently doing jobs where no one notices them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JAMES:&lt;/strong&gt; That’s a great line.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SUPREET:&lt;/strong&gt; It’s also true.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;JAMES:&lt;/strong&gt; &lt;em&gt;(lifts coffee)&lt;/em&gt; So—hire potential, mentor properly, grow your own talent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SUPREET:&lt;/strong&gt; &lt;em&gt;(raises cup back)&lt;/em&gt; And stop waiting for unicorns that don’t exist.&lt;/p&gt;

</description>
      <category>learning</category>
      <category>hiring</category>
    </item>
    <item>
      <title>Forget Wall Street: Here’s How to Create the Best Stock Screener Yourself</title>
      <dc:creator>Supreet Sethi</dc:creator>
      <pubDate>Thu, 27 Nov 2025 06:19:03 +0000</pubDate>
      <link>https://dev.to/djinn/forget-wall-street-heres-how-to-create-the-best-stock-screener-yourself-4k2i</link>
      <guid>https://dev.to/djinn/forget-wall-street-heres-how-to-create-the-best-stock-screener-yourself-4k2i</guid>
      <description>&lt;h1&gt;
  
  
  Stock Screeners for Math People: Build Your Own
&lt;/h1&gt;

&lt;p&gt;A stock screener is just a function. You don't need Wall Street, a fancy terminal, or YouTube. If you know high school math and Python, you can build your own screener that is transparent, tuned to your portfolio, and changeable as your ideas evolve.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. What Is a Stock Screener, Really?
&lt;/h2&gt;

&lt;p&gt;At its core, a stock screener is a &lt;strong&gt;mathematical filter function&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;

&lt;/p&gt;
&lt;div class="katex-element"&gt;
  &lt;span class="katex-display"&gt;&lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;Screener(stock)={TRUEif stock passes your rules FALSEotherwise
\text{Screener}(\text{stock}) = \begin{cases} \text{TRUE} &amp;amp; \text{if stock passes your rules} \ \text{FALSE} &amp;amp; \text{otherwise} \end{cases}
&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;Screener&lt;/span&gt;&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;stock&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="minner"&gt;&lt;span class="mopen delimcenter"&gt;&lt;span class="delimsizing size2"&gt;{&lt;/span&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mtable"&gt;&lt;span class="col-align-l"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;TRUE&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="arraycolsep"&gt;&lt;/span&gt;&lt;span class="col-align-l"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;if stock passes your rules&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt; &lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;FALSE&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="arraycolsep"&gt;&lt;/span&gt;&lt;span class="col-align-c"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;otherwise&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose nulldelimiter"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/div&gt;


&lt;p&gt;You have a universe of &lt;em&gt;N&lt;/em&gt; stocks, each with data:&lt;/p&gt;


&lt;div class="katex-element"&gt;
  &lt;span class="katex-display"&gt;&lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;Input=S1,S2,…,SN,Si=Pi(t),Vi(t),Fi
\text{Input} = {S_1, S_2, \ldots, S_N}, \quad S_i = {P_i(t), V_i(t), F_i}
&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;Input&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;S&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mpunct"&gt;,&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;S&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mpunct"&gt;,&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="minner"&gt;…&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mpunct"&gt;,&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;S&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;N&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mpunct"&gt;,&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;S&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;P&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;t&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;span class="mpunct"&gt;,&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;V&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;t&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;span class="mpunct"&gt;,&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;F&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/div&gt;


&lt;p&gt;Where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;Pi(t)P_i(t)&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;P&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;t&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 = price of stock &lt;em&gt;i&lt;/em&gt; at time &lt;em&gt;t&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;Vi(t)V_i(t)&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;V&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;t&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 = trading volume&lt;/li&gt;
&lt;li&gt;
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;FiF_i&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;F&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 = fundamental data (EPS, revenue, etc.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your screener applies a function:&lt;/p&gt;


&lt;div class="katex-element"&gt;
  &lt;span class="katex-display"&gt;&lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;f(Pi(t),Vi(t),Fi)→TRUE,FALSE
f(P_i(t), V_i(t), F_i) \rightarrow {\text{TRUE}, \text{FALSE}}
&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;f&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;P&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;t&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;span class="mpunct"&gt;,&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;V&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;t&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;span class="mpunct"&gt;,&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;F&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;→&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;TRUE&lt;/span&gt;&lt;/span&gt;&lt;span class="mpunct"&gt;,&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;FALSE&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Apply this to 5,000 stocks, keep those returning &lt;code&gt;TRUE&lt;/code&gt;, then rank them. That's the entire idea.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Why the "Best Screener" Depends on What You Already Own
&lt;/h2&gt;

&lt;p&gt;The biggest mistake beginners make: treating the "best stock screener" as universal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In reality, it depends on your portfolio vector:&lt;/strong&gt;&lt;/p&gt;


&lt;div class="katex-element"&gt;
  &lt;span class="katex-display"&gt;&lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;P⃗=[p1,p2,…,pn]
\vec{P} = [p_1, p_2, \ldots, p_n]
&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord accent"&gt;&lt;span class="vlist-t"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;P&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="accent-body"&gt;&lt;span class="overlay"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mopen"&gt;[&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;p&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mpunct"&gt;,&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;p&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mpunct"&gt;,&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="minner"&gt;…&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mpunct"&gt;,&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;p&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose"&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/div&gt;


&lt;p&gt;Each 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;pip_i&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;p&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 is a stock with its own return series. When you add a new stock 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;XX&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;X&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
, what matters is &lt;strong&gt;correlation with your existing portfolio&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Correlation Between Return Series
&lt;/h3&gt;


&lt;div class="katex-element"&gt;
  &lt;span class="katex-display"&gt;&lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;Corr(X,Y)=Cov(X,Y)σXσY
\text{Corr}(X, Y) = \frac{\text{Cov}(X, Y)}{\sigma_X \sigma_Y}
&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;Corr&lt;/span&gt;&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;X&lt;/span&gt;&lt;span class="mpunct"&gt;,&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;Y&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen nulldelimiter"&gt;&lt;/span&gt;&lt;span class="mfrac"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;σ&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;X&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;σ&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;Y&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="frac-line"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;Cov&lt;/span&gt;&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;X&lt;/span&gt;&lt;span class="mpunct"&gt;,&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;Y&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose nulldelimiter"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/div&gt;


&lt;p&gt;The interpretation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;Corr≈1\text{Corr} \approx 1&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;Corr&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;≈&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 → they move together (bad for diversification)&lt;/li&gt;
&lt;li&gt;
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;Corr≈0\text{Corr} \approx 0&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;Corr&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;≈&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 → independent movements (good)&lt;/li&gt;
&lt;li&gt;
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;Corr≈−1\text{Corr} \approx -1&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;Corr&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;≈&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;−&lt;/span&gt;&lt;span class="mord"&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 → opposite moves (excellent hedge)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Real Problem
&lt;/h3&gt;

&lt;p&gt;If you already own 10 tech stocks with mutual correlation ≈ 0.8, adding an 11th tech stock &lt;strong&gt;just concentrates risk&lt;/strong&gt;. It's not diversification—it's doubling down.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your screener must know what you own and prefer low-correlation stocks.&lt;/strong&gt;&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;numpy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pandas&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;portfolio_returns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;returns_df&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Compute weighted portfolio return series&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;reshape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;returns_df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;flatten&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;correlation_with_portfolio&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;candidate_returns&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;portfolio_ret_series&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Correlation between a candidate and your portfolio&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;corr_matrix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;corrcoef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;candidate_returns&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;portfolio_ret_series&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;corr_matrix&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# Decision rule: only buy if correlation &amp;lt; 0.5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Core Math Building Blocks
&lt;/h2&gt;
&lt;h3&gt;
  
  
  3.1 Returns: Rate of Change
&lt;/h3&gt;

&lt;p&gt;Return over &lt;em&gt;n&lt;/em&gt; days is the percentage change:&lt;/p&gt;


&lt;div class="katex-element"&gt;
  &lt;span class="katex-display"&gt;&lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;Rn(t)=P(t)−P(t−n)P(t−n)
R_n(t) = \frac{P(t) - P(t-n)}{P(t-n)}
&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;R&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;t&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen nulldelimiter"&gt;&lt;/span&gt;&lt;span class="mfrac"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;P&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;t&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;−&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="frac-line"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;P&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;t&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;−&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;P&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;t&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;−&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose nulldelimiter"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Price 20 days ago: $100&lt;/li&gt;
&lt;li&gt;Price today: $115&lt;/li&gt;
&lt;li&gt;20-day return: 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;115−100100=0.15=15%\frac{115 - 100}{100} = 0.15 = 15\%&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen nulldelimiter"&gt;&lt;/span&gt;&lt;span class="mfrac"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;&lt;span class="mord mtight"&gt;100&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="frac-line"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;&lt;span class="mord mtight"&gt;115&lt;/span&gt;&lt;span class="mbin mtight"&gt;−&lt;/span&gt;&lt;span class="mord mtight"&gt;100&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose nulldelimiter"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;0.15&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;15%&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3.2 Moving Averages: Smoothing Noise
&lt;/h3&gt;

&lt;p&gt;Simple Moving Average over &lt;em&gt;n&lt;/em&gt; days:&lt;/p&gt;


&lt;div class="katex-element"&gt;
  &lt;span class="katex-display"&gt;&lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;SMAn(t)=1n∑i=0n−1P(t−i)
\text{SMA}n(t) = \frac{1}{n} \sum{i=0}^{n-1} P(t-i)
&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;SMA&lt;/span&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;t&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen nulldelimiter"&gt;&lt;/span&gt;&lt;span class="mfrac"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="frac-line"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose nulldelimiter"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mop op-symbol large-op"&gt;∑&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;i&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord"&gt;0&lt;/span&gt;&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;n&lt;/span&gt;&lt;span class="mbin mtight"&gt;−&lt;/span&gt;&lt;span class="mord mtight"&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;P&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;t&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;−&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;i&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/div&gt;


&lt;p&gt;If price is &lt;strong&gt;above&lt;/strong&gt; its recent SMA, it often signals an uptrend.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.3 Volume Ratio: Conviction Metric
&lt;/h3&gt;

&lt;p&gt;Volume tells you "how many people agree with the move":&lt;/p&gt;


&lt;div class="katex-element"&gt;
  &lt;span class="katex-display"&gt;&lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;Volume Ratio=Recent Average VolumeHistorical Average Volume
\text{Volume Ratio} = \frac{\text{Recent Average Volume}}{\text{Historical Average Volume}}
&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;Volume Ratio&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen nulldelimiter"&gt;&lt;/span&gt;&lt;span class="mfrac"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;Historical Average Volume&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="frac-line"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;Recent Average Volume&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose nulldelimiter"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/div&gt;


&lt;p&gt;If this is 
&lt;span class="katex-element"&gt;
  ParseError: KaTeX parse error: Expected 'EOF', got '}' at position 1: }̲ &amp;gt; 1.5
&lt;/span&gt;
, volume is picking up—a strong signal.&lt;/p&gt;


&lt;h2&gt;
  
  
  4. Your Data: CSV + SQLite
&lt;/h2&gt;

&lt;p&gt;Use two simple data sources you control locally:&lt;/p&gt;
&lt;h3&gt;
  
  
  Daily Prices in CSV
&lt;/h3&gt;

&lt;p&gt;One file per ticker: &lt;code&gt;AAPL.csv&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Date,Open,High,Low,Close,Volume
2024-01-15,150.00,152.50,149.80,151.20,2500000
2024-01-16,151.30,153.00,151.00,152.80,2300000
2024-01-17,152.00,153.80,151.50,152.95,2400000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Quarterly Fundamentals in SQLite
&lt;/h3&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;sqlite3&lt;/span&gt;

&lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sqlite3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stocks.db&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
CREATE TABLE IF NOT EXISTS fundamentals (
    ticker TEXT,
    quarter_date TEXT,
    eps REAL,
    estimated_eps REAL,
    revenue REAL,
    net_income REAL,
    market_cap REAL
)
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
CREATE TABLE IF NOT EXISTS companies (
    ticker TEXT PRIMARY KEY,
    name TEXT,
    sector TEXT
)
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;You now have a &lt;strong&gt;local data warehouse you control&lt;/strong&gt;—no API limits, fully reproducible.&lt;/p&gt;


&lt;h2&gt;
  
  
  5. Screener #1: Momentum (First Derivative of Price)
&lt;/h2&gt;

&lt;p&gt;Momentum is just rate of change:&lt;/p&gt;


&lt;div class="katex-element"&gt;
  &lt;span class="katex-display"&gt;&lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;Momentumn(t)=P(t)−P(t−n)P(t−n)⋅100%
\text{Momentum}_n(t) = \frac{P(t) - P(t-n)}{P(t-n)} \cdot 100\%
&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;Momentum&lt;/span&gt;&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;t&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen nulldelimiter"&gt;&lt;/span&gt;&lt;span class="mfrac"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;P&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;t&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;−&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="frac-line"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;P&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;t&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;−&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;P&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;t&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;−&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose nulldelimiter"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;⋅&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;100%&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/div&gt;



&lt;p&gt;A simple rule: select stocks with 20-day momentum 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;&amp;gt;15%\gt 15\%&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;15%&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
.&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;pandas&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;numpy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;load_price_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;csv_dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ticker&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Load CSV into DataFrame&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;csv_dir&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ticker&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.csv&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Date&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_datetime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Date&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Date&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;momentum_20d&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;close_series&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;period&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Calculate n-day momentum percentage&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;close_series&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;period&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nan&lt;/span&gt;
    &lt;span class="n"&gt;p_today&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;close_series&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;iloc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;p_past&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;close_series&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;iloc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;period&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nf"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p_today&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;p_past&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;p_past&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;100.0&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;momentum_screen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;csv_dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;min_mom_pct&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Screen: momentum &amp;gt; 15%&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;tickers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;csv_dir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
               &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;endswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.csv&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;tickers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_price_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;csv_dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;mom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;momentum_20d&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Close&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;mom&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;min_mom_pct&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ticker&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;momentum_20d_pct&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mom&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Error on &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;momentum_20d_pct&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;reverse&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Run it
&lt;/span&gt;&lt;span class="n"&gt;momentum_stocks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;momentum_screen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./stock_data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Found &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;momentum_stocks&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; momentum candidates&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;When to use:&lt;/strong&gt; When you see a trend forming. Momentum works because markets overshoot—once they start moving, they keep moving.&lt;/p&gt;


&lt;h2&gt;
  
  
  6. Screener #2: Volume Accumulation (Smart Money Signal)
&lt;/h2&gt;

&lt;p&gt;We want to know if price moves are backed by serious trading volume.&lt;/p&gt;

&lt;p&gt;The money flow line combines two facts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Where did price close in today's range?&lt;/li&gt;
&lt;li&gt;How much volume was there?&lt;/li&gt;
&lt;/ul&gt;


&lt;div class="katex-element"&gt;
  &lt;span class="katex-display"&gt;&lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;MoneyFlow(t)=Close(t)−Low(t)High(t)−Low(t)⋅Volume(t)
\text{MoneyFlow}(t) = \frac{\text{Close}(t) - \text{Low}(t)}{\text{High}(t) - \text{Low}(t)} \cdot \text{Volume}(t)
&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;MoneyFlow&lt;/span&gt;&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;t&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen nulldelimiter"&gt;&lt;/span&gt;&lt;span class="mfrac"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;High&lt;/span&gt;&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;t&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;−&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;Low&lt;/span&gt;&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;t&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="frac-line"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;Close&lt;/span&gt;&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;t&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;−&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;Low&lt;/span&gt;&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;t&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose nulldelimiter"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;⋅&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;Volume&lt;/span&gt;&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;t&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/div&gt;



&lt;p&gt;If this is consistently positive AND volume is rising, you likely have accumulation.&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;volume_ratio&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;recent_days&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hist_days&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Volume momentum: recent average / historical average&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;recent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Volume&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;tail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;recent_days&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;hist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Volume&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;tail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hist_days&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;recent&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;hist&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;hist&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;money_flow_sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;period&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Sum of money flow over period&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;high&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;High&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;low&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Low&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;close&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Close&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;vol&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Volume&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;hl_range&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;high&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;low&lt;/span&gt;
    &lt;span class="n"&gt;position&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;hl_range&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;low&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;hl_range&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="mf"&gt;0.5&lt;/span&gt;  &lt;span class="c1"&gt;# Avoid division by zero
&lt;/span&gt;    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;flow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;position&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;vol&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;flow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;period&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;accumulation_screen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;csv_dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;min_vol_ratio&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;1.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;min_price&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;5.0&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Screen: rising volume + positive money flow&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;tickers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;csv_dir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
               &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;endswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.csv&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;tickers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_price_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;csv_dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Close&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;iloc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;min_price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;continue&lt;/span&gt;

            &lt;span class="n"&gt;vr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;volume_ratio&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;mf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;money_flow_sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;vr&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;min_vol_ratio&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;mf&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ticker&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;volume_ratio&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;money_flow&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Error on &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;volume_ratio&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;reverse&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Run it
&lt;/span&gt;&lt;span class="n"&gt;accum_stocks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;accumulation_screen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./stock_data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Found &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;accum_stocks&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; accumulation candidates&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;When to use:&lt;/strong&gt; When you want to catch early moves before the broader market notices. Volume leads price.&lt;/p&gt;


&lt;h2&gt;
  
  
  7. Screener #3: Earnings Surprise (When Reality Beats Expectations)
&lt;/h2&gt;

&lt;p&gt;Markets react strongly when earnings differ from estimates.&lt;/p&gt;


&lt;div class="katex-element"&gt;
  &lt;span class="katex-display"&gt;&lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;Surprise%=EPSactual−EPSestimateEPSestimate⋅100
\text{Surprise\%} = \frac{\text{EPS}{\text{actual}} - \text{EPS}{\text{estimate}}}{\text{EPS}_{\text{estimate}}} \cdot 100
&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;Surprise%&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen nulldelimiter"&gt;&lt;/span&gt;&lt;span class="mfrac"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;EPS&lt;/span&gt;&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;&lt;span class="mord text mtight"&gt;&lt;span class="mord mtight"&gt;estimate&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="frac-line"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;EPS&lt;/span&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;actual&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;−&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;EPS&lt;/span&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;estimate&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose nulldelimiter"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;⋅&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;100&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/div&gt;



&lt;p&gt;Positive surprises often lead to repricing.&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;sqlite3&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_latest_earnings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ticker&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Fetch most recent earnings from database&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;cur&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
        SELECT quarter_date, eps, estimated_eps
        FROM fundamentals
        WHERE ticker = ?
        ORDER BY quarter_date DESC
        LIMIT 1
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ticker&lt;/span&gt;&lt;span class="p"&gt;,))&lt;/span&gt;

    &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchone&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

    &lt;span class="n"&gt;qdate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;eps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;est&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;
    &lt;span class="n"&gt;surprise_pct&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;eps&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;est&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;est&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;est&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;date&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;qdate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;eps&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;eps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;est&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;est&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;surprise_pct&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;surprise_pct&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;earnings_surprise_screen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;csv_dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;db_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                            &lt;span class="n"&gt;min_surprise&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_days_old&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Screen: positive recent earnings surprise&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sqlite3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;tickers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;csv_dir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
               &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;endswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.csv&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;tickers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_latest_earnings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;continue&lt;/span&gt;

            &lt;span class="n"&gt;days_old&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; 
                       &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromisoformat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;date&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])).&lt;/span&gt;&lt;span class="n"&gt;days&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;surprise_pct&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;min_surprise&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;days_old&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;max_days_old&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ticker&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;surprise_pct&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;surprise_pct&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;days_since_earnings&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;days_old&lt;/span&gt;
                &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Error on &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;surprise_pct&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;reverse&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Run it
&lt;/span&gt;&lt;span class="n"&gt;earnings_stocks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;earnings_surprise_screen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./stock_data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stocks.db&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Found &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;earnings_stocks&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; earnings surprise candidates&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;When to use:&lt;/strong&gt; Right after earnings season. Markets often misprice surprises—you're looking for the market catching up.&lt;/p&gt;


&lt;h2&gt;
  
  
  8. Screener #4: Valuation (Price Per Unit of Business)
&lt;/h2&gt;

&lt;p&gt;Simple valuation metrics avoid overpaying.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Price-to-Sales Ratio:&lt;/strong&gt;&lt;/p&gt;


&lt;div class="katex-element"&gt;
  &lt;span class="katex-display"&gt;&lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;P/S=Market CapAnnual Revenue
\text{P/S} = \frac{\text{Market Cap}}{\text{Annual Revenue}}
&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;P/S&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen nulldelimiter"&gt;&lt;/span&gt;&lt;span class="mfrac"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;Annual Revenue&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="frac-line"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;Market Cap&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose nulldelimiter"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Price-to-Earnings Ratio:&lt;/strong&gt;&lt;/p&gt;


&lt;div class="katex-element"&gt;
  &lt;span class="katex-display"&gt;&lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;P/E=Market CapNet Income
\text{P/E} = \frac{\text{Market Cap}}{\text{Net Income}}
&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;P/E&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen nulldelimiter"&gt;&lt;/span&gt;&lt;span class="mfrac"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;Net Income&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="frac-line"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;Market Cap&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose nulldelimiter"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/div&gt;


&lt;p&gt;Lower ratios are cheaper. But cheap can mean "dying business", so combine with growth/momentum.&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_latest_fundamentals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ticker&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Fetch latest quarter fundamentals&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;cur&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
        SELECT market_cap, revenue, net_income
        FROM fundamentals
        WHERE ticker = ?
        ORDER BY quarter_date DESC
        LIMIT 1
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ticker&lt;/span&gt;&lt;span class="p"&gt;,))&lt;/span&gt;

    &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchone&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;market_cap&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;revenue&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;net_income&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;valuation_screen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;csv_dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;db_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_ps&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                     &lt;span class="n"&gt;max_pe&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;15.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;min_price&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;5.0&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Screen: low P/S and P/E ratios&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sqlite3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;tickers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;csv_dir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
               &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;endswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.csv&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;tickers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_latest_fundamentals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;continue&lt;/span&gt;

            &lt;span class="n"&gt;mc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;market_cap&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="n"&gt;rev&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;revenue&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="n"&gt;ni&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;net_income&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

            &lt;span class="n"&gt;ps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mc&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;rev&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;rev&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;inf&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;pe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mc&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;ni&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ni&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;inf&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ps&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;max_ps&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;pe&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;max_pe&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;continue&lt;/span&gt;

            &lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_price_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;csv_dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Close&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;iloc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;min_price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;continue&lt;/span&gt;

            &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ticker&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ps&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pe&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pe&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;price&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Error on &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ps&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="c1"&gt;# Run it
&lt;/span&gt;&lt;span class="n"&gt;value_stocks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;valuation_screen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./stock_data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stocks.db&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Found &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value_stocks&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; value candidates&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;When to use:&lt;/strong&gt; When you want a "margin of safety"—cheap stocks provide cushion if things go wrong.&lt;/p&gt;


&lt;h2&gt;
  
  
  9. Screener #5: Combined Signal (Weighted Score)
&lt;/h2&gt;

&lt;p&gt;Now you have four independent signals. Combine them with a &lt;strong&gt;weighted linear combination&lt;/strong&gt;:&lt;/p&gt;


&lt;div class="katex-element"&gt;
  &lt;span class="katex-display"&gt;&lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;Score=w1s1+w2s2+w3s3+w4s4
\text{Score} = w_1 s_1 + w_2 s_2 + w_3 s_3 + w_4 s_4
&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;Score&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;w&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;s&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;w&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;s&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;w&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;3&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;s&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;3&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;w&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;4&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;s&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;4&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/div&gt;



&lt;p&gt;Where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;sis_i&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;s&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 = normalized score from signal &lt;em&gt;i&lt;/em&gt; (scaled to 0–1 range)&lt;/li&gt;
&lt;li&gt;
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;wiw_i&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;w&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 = weight for signal &lt;em&gt;i&lt;/em&gt; (must sum to 1)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example: equal weights 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;wi=0.25w_i = 0.25&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;w&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;0.25&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 for each signal.&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;normalize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;min_val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;invert&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Normalize value to [0, 1] range&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;max_val&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;min_val&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;min_val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_val&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;min_val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  &lt;span class="c1"&gt;# Clamp to [0, 1]
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;invert&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;combine_signals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;momentum_dict&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;volume_dict&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                   &lt;span class="n"&gt;earnings_dict&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;valuation_dict&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Combine four signals into one composite score&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;all_tickers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;momentum_dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;volume_dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; 
                   &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;earnings_dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;valuation_dict&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;scores&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;all_tickers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

        &lt;span class="c1"&gt;# Momentum: 0 to 50 percent (25% weight)
&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;momentum_dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;momentum_dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nf"&gt;normalize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;

        &lt;span class="c1"&gt;# Volume ratio: 1 to 3 (25% weight)
&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;volume_dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;volume_dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nf"&gt;normalize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;

        &lt;span class="c1"&gt;# Earnings surprise: 0 to 50 percent (25% weight)
&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;earnings_dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;earnings_dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nf"&gt;normalize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;

        &lt;span class="c1"&gt;# Valuation: lower P/S better, inverted (25% weight)
&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;valuation_dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;ps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;valuation_dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nf"&gt;normalize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;invert&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;

        &lt;span class="n"&gt;scores&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt;

    &lt;span class="n"&gt;ranked&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scores&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;reverse&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ticker&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;score&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ranked&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# Run all screeners and combine
&lt;/span&gt;&lt;span class="n"&gt;momentum_results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;momentum_screen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./stock_data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;volume_results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;accumulation_screen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./stock_data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;earnings_results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;earnings_surprise_screen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./stock_data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stocks.db&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;valuation_results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;valuation_screen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./stock_data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stocks.db&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Convert to dictionaries keyed by ticker
&lt;/span&gt;&lt;span class="n"&gt;momentum_dict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ticker&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;momentum_20d_pct&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;momentum_results&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;volume_dict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ticker&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;volume_ratio&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;volume_results&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;earnings_dict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ticker&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;surprise_pct&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;earnings_results&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;valuation_dict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ticker&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ps&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;valuation_results&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Get composite scores
&lt;/span&gt;&lt;span class="n"&gt;combined&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;combine_signals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;momentum_dict&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;volume_dict&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                          &lt;span class="n"&gt;earnings_dict&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;valuation_dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;🏆 TOP 10 STOCKS (COMPOSITE SCORE):&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;combined&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;. &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ticker&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; — Score: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;score&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mf"&gt;6.1&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/100&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;You now have your own best stock screener&lt;/strong&gt; that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Uses clear math&lt;/li&gt;
&lt;li&gt;Uses data you control&lt;/li&gt;
&lt;li&gt;Can be tuned in a few lines of code&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  10. Portfolio Context: Which Screener Should You Emphasize?
&lt;/h2&gt;

&lt;p&gt;You don't have to use all signals equally. Your weights depend on what you own.&lt;/p&gt;

&lt;h3&gt;
  
  
  If you own bonds + gold (defensive)
&lt;/h3&gt;

&lt;p&gt;Focus on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Momentum&lt;/strong&gt; (high weight)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Volume accumulation&lt;/strong&gt; (high weight)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Logic: You want high-growth, high-beta assets to offset your defensive base.&lt;/p&gt;

&lt;h3&gt;
  
  
  If you already own a lot of tech
&lt;/h3&gt;

&lt;p&gt;Focus on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Valuation&lt;/strong&gt; (high weight)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Correlation filter&lt;/strong&gt; (not a weight, but mandatory)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Logic: Reduce concentration risk rather than stacking more tech. Screen for uncorrelated sectors.&lt;/p&gt;

&lt;h3&gt;
  
  
  If you're just starting
&lt;/h3&gt;

&lt;p&gt;Use equal weights (25% each).&lt;/p&gt;

&lt;p&gt;Then gradually shift weights as you learn what matches your trading style.&lt;/p&gt;




&lt;h2&gt;
  
  
  11. What Your Broker Won't Tell You
&lt;/h2&gt;

&lt;p&gt;Most broker "stock screener" tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;❌ Use fixed rules not tuned to your portfolio&lt;/li&gt;
&lt;li&gt;❌ Don't show you the actual math&lt;/li&gt;
&lt;li&gt;❌ Encourage overtrading with flashy filters&lt;/li&gt;
&lt;li&gt;❌ Change rules without telling you&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;You can do better:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Write the math yourself&lt;/li&gt;
&lt;li&gt;✅ Implement it in Python&lt;/li&gt;
&lt;li&gt;✅ Run it weekly on your own data&lt;/li&gt;
&lt;li&gt;✅ Check correlation before buying&lt;/li&gt;
&lt;li&gt;✅ Keep a log of rule changes and results&lt;/li&gt;
&lt;li&gt;✅ Know exactly what you own&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  12. Quick Checklist: Your Own Stock Screener
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;[ ] You understand inputs: price, volume, fundamentals&lt;/li&gt;
&lt;li&gt;[ ] You compute returns with 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;Rn(t)=P(t)−P(t−n)P(t−n)R_n(t) = \frac{P(t) - P(t-n)}{P(t-n)}&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;R&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;t&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen nulldelimiter"&gt;&lt;/span&gt;&lt;span class="mfrac"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;P&lt;/span&gt;&lt;span class="mopen mtight"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal mtight"&gt;t&lt;/span&gt;&lt;span class="mbin mtight"&gt;−&lt;/span&gt;&lt;span class="mord mathnormal mtight"&gt;n&lt;/span&gt;&lt;span class="mclose mtight"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="frac-line"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;P&lt;/span&gt;&lt;span class="mopen mtight"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal mtight"&gt;t&lt;/span&gt;&lt;span class="mclose mtight"&gt;)&lt;/span&gt;&lt;span class="mbin mtight"&gt;−&lt;/span&gt;&lt;span class="mord mathnormal mtight"&gt;P&lt;/span&gt;&lt;span class="mopen mtight"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal mtight"&gt;t&lt;/span&gt;&lt;span class="mbin mtight"&gt;−&lt;/span&gt;&lt;span class="mord mathnormal mtight"&gt;n&lt;/span&gt;&lt;span class="mclose mtight"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose nulldelimiter"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
&lt;/li&gt;
&lt;li&gt;[ ] You use moving averages to smooth noise&lt;/li&gt;
&lt;li&gt;[ ] You confirm moves with volume (not price alone)&lt;/li&gt;
&lt;li&gt;[ ] You check earnings surprise (not just headlines)&lt;/li&gt;
&lt;li&gt;[ ] You use valuation ratios to avoid overpaying&lt;/li&gt;
&lt;li&gt;[ ] You combine signals into a weighted score&lt;/li&gt;
&lt;li&gt;[ ] You check correlation with your existing portfolio&lt;/li&gt;
&lt;li&gt;[ ] You only change rules slowly and record impact&lt;/li&gt;
&lt;li&gt;[ ] You keep your data local (CSV + SQLite)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Set up your data pipeline&lt;/strong&gt;: Download historical CSV files for 50–100 stocks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Populate SQLite&lt;/strong&gt;: Add quarterly fundamentals from public sources&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Run each screener independently&lt;/strong&gt;: Build intuition for what each detects&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Combine with your weights&lt;/strong&gt;: Start with equal weights, then tune&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backtest&lt;/strong&gt;: Log your picks and track win rates&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Iterate&lt;/strong&gt;: Adjust weights monthly based on what works&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Good luck.&lt;/p&gt;

</description>
      <category>stocks</category>
      <category>cryptocurrency</category>
      <category>python</category>
      <category>pandas</category>
    </item>
    <item>
      <title>Can simple 4 Core, 16 GB RAM reach 1000 tps?</title>
      <dc:creator>Supreet Sethi</dc:creator>
      <pubDate>Thu, 24 Apr 2025 07:14:23 +0000</pubDate>
      <link>https://dev.to/djinn/can-simple-4-core-16-gb-ram-reach-1000-tps-5pl</link>
      <guid>https://dev.to/djinn/can-simple-4-core-16-gb-ram-reach-1000-tps-5pl</guid>
      <description>&lt;p&gt;Databases are tricky. They hold more state as we move towards stateless architectures. Kubernetes setups often start with stated intention of providing governance over all infrastructure until they encounter Databases. Database also turns out to be the largest cost in many workloads, because of compute requirements, DR, backup and skill. &lt;/p&gt;

&lt;p&gt;Database costs are fixed, hard to reduce and form almost 1/3rd of Cloud costs for many customers. As a cofounder of startup or VP of Cloud in a Bank, you start to think of this cost item as the cost of doing business and this has always been. Until it isn't. Database like any other application is highly configurable and we look at major levers to increase performance where a 138$ a month database performs like a $3000 a month. A notional &lt;em&gt;Porsche 917K&lt;/em&gt; at price of &lt;em&gt;Renault Clio&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;We cannot start from managed options here clearly here because it it just a more expensive start. Many of the activities and learnings are deployable on managed databases but we want to start at garage sale price for our shiny high octane database. We start with an ordinary node and in case of E2E Cloud with a 4 core 16 GB c3.16GB-288 instance which costs $66 a month. &lt;/p&gt;

&lt;p&gt;We will truly snowflake the database by naming the server david. We will turn these learnings in K8S setup later desnowflaking it but for now we want it - to be one of a kind.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo hostnamectl set-hostname David
$ sudo sed -i "s/$(hostname)/david/g" /etc/hosts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's setup SysBench as our reference benchmark to test david like a dojo testing a pup pupil until it reaches mental and physical toughness. &lt;/p&gt;

&lt;p&gt;Firstly we will setup MySQL and SysBench for testing and run the control test cycle. Control provides us the baseline performance, which will show us how much we were able to push the machine to the frontier of becoming a $3000 dollar DB server. &lt;/p&gt;

&lt;p&gt;david$ sudo apt install sysbench mysql-server&lt;br&gt;
I will leave setting credentials on MySQL to the reader. We run the benchmark like this;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;david$ sysbench oltp_read_write \
    --tables=10 \
    --table-size=1000000 \
    --threads=4 \
    --time=60 \
    --db-driver=mysql \
    --mysql-db=sbtest \
    --mysql-user=sbtest \
    --mysql-password=password \
    run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will anchor the readings as a baseline here with key items that matter in read/write mixed performance&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Transaction/Sec which baselines at 112.24 (higher is better)&lt;/li&gt;
&lt;li&gt;Queries/Sec at 2,244.84 (higher is better)&lt;/li&gt;
&lt;li&gt;Avg Latency (ms) 35.63 (lower is better)&lt;/li&gt;
&lt;li&gt;95th %ile Latency (ms) 94.10 (lower is better)&lt;/li&gt;
&lt;li&gt;Min Latency (ms) 4.31 (lower is better)&lt;/li&gt;
&lt;li&gt;Max Latency (ms) 1,065.46 (lower is better)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We will be improving on each of these key performance numbers as we go through retrofitting for Porsche makeover. Before saying that we improve these numbers, we should level-set that these are quite good numbers to start and often meet or exceed customer requirements in vast majority of use-cases. &lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: InnoDB Block Sizes and File I/O
&lt;/h2&gt;

&lt;p&gt;Block sizes of choice in Database and Filesystem is single most important change that you could do to improve the database performance. Ostensibly XFS is the best filesystem for database performance however we can't larger block sizes beyond 4k because that requires Linux kernel recompilation. However if we place Innodb block size to be 16k that aligns neatly with 4 blocks for 4k size thus it is still a performance win.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Add to /etc/mysql/mysql.conf.d/mysqld.cnf  
innodb_page_size=16384

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa5cwzsc2zjy73fyw6m2q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa5cwzsc2zjy73fyw6m2q.png" alt="Vanilla vs InnoDB Block Size Change" width="800" height="637"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With a single line change we have almost doubled our performance from 66$ instance. To reach this performance without config change we would have expended a budget of 3 times current spend. We have thus increased value of our setup by $132. &lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Swapiness and network parameters
&lt;/h2&gt;

&lt;p&gt;Reference Only – Not Applied in Benchmarks&lt;/p&gt;

&lt;p&gt;There are several popular Swap and network related sysctl parameters which change kernel behaviour at runtime. Network ones are irrelevant since in my setup networking is loopback which is sufficiently fast in normal coarse. Swap settings are risky, or harmful or both in virtualised environment. We include them for sake of completeness but we did not apply and test in our environment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# MySQL Swap Settings
vm.swappiness = 10
vm.vfs_cache_pressure = 50

# File System Settings
fs.file-max = 65536
fs.aio-max-nr = 1048576

# Network Settings - General
net.core.somaxconn = 4096
net.core.netdev_max_backlog = 4000
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216

# TCP Settings
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_keepalive_time = 300
net.ipv4.tcp_keepalive_probes = 5
net.ipv4.tcp_keepalive_intvl = 15
net.ipv4.tcp_max_syn_backlog = 4096
net.ipv4.tcp_tw_reuse = 1
net.ipv4.ip_local_port_range = 10000 65000
net.ipv4.tcp_slow_start_after_idle = 0

# Memory Management for Database Workloads
vm.dirty_ratio = 15
vm.dirty_background_ratio = 5
vm.dirty_expire_centisecs = 500
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is imperative that you should be testing your workload with each of these changes before ever bringing them to production. &lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: MySQL tune-ups
&lt;/h2&gt;

&lt;p&gt;MySQL has history of being used as database of the Internet. Almost every site large and small hosts on MySQL. With that there a myriad of features, bells and whistles, dials and buttons. We decided to press few of these buttons to extract MySQL performance.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Add to /etc/mysql/mysql.conf.d/mysqld.cnf  

# InnoDB Buffer Pool Settings
innodb_buffer_pool_size = 12G
innodb_buffer_pool_instances = 4
innodb_old_blocks_time = 1000

# Transaction and Log Settings
innodb_log_file_size = 3G
innodb_log_buffer_size = 32M
innodb_flush_log_at_trx_commit = 1
sync_binlog = 1

# I/O and Threading
innodb_io_capacity = 2000
innodb_io_capacity_max = 4000
innodb_read_io_threads = 4
innodb_write_io_threads = 4
innodb_page_cleaners = 4

# File and Storage Settings
innodb_flush_method = O_DIRECT
innodb_file_per_table = ON
innodb_open_files = 400
table_definition_cache = 1400
table_open_cache = 2000

# Query Optimization
join_buffer_size = 2M
sort_buffer_size = 4M
read_rnd_buffer_size = 2M
read_buffer_size = 1M
tmp_table_size = 64M
max_heap_table_size = 64M

# Connection and Thread Settings
max_connections = 150
thread_cache_size = 16
max_prepared_stmt_count = 16384
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These numbers have been worked out on specific CPU, RAM and Disk Configuration we used. We assumed that MySQL will be the only tenant to this system and it can fully utilise the resources for maximum performance. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4aa23jzgx5xawmdiiuqk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4aa23jzgx5xawmdiiuqk.png" alt="Vanilla vs InnoDB Block Size vs MySQL Tune-up" width="800" height="637"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With these valuable config change, we once again doubled the performance of our system which still is being charged at $66 per month. Now notional the value we extract out of it is close to 16 core system which could cost us around $660, unlocking value of $600. &lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Deadlines and IO Schedulers
&lt;/h2&gt;

&lt;p&gt;IO schedulers are important but not that important in SSD and NVMe world today. Both these kind of devices have sophisticated way to schedule the IO workload and placing priority on them. While Ubuntu by default includes two schedulers none and mq-deadline. We stick to default. No changes. No performance gains. &lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: More NVMe Controllers, More IOPS, More Throughput
&lt;/h2&gt;

&lt;p&gt;At this time, the simplest thing to do is add more volumes which increase IOPS and throughput. We have two options for doing it, first and simplest is to put single large disk and reap high IOPS, &lt;/p&gt;

&lt;p&gt;256 GB -&amp;gt; 5000 IOPS&lt;br&gt;
512 GB -&amp;gt; 10000 IOPS&lt;br&gt;
1TB -&amp;gt; 20000 IOPS&lt;/p&gt;

&lt;p&gt;The second and more interesting method is to setup RAID - Redundant Array of Inexpensive Disks or in this Redundant Array of Inexpensive NVMe (RAIN). We have to increase our budget by $66 to do this. We take 4 256 GB Volumes at E2E Cloud priced at 8 dollars per Volume per month. Thus our revised total cost of ownership for this new setup is $138 per month. Once attached these volumes will show up as available disks vd*&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;david$ lsblk

NAME   MAJ:MIN RM   SIZE RO TYPE MOUNTPOINTS
sr0     11:0    1   366K  0 rom
vda    253:0    0 139.7G  0 disk
├─vda1 253:1    0     1M  0 part
└─vda2 253:2    0 139.7G  0 part /
vdb    253:16   0 232.8G  0 disk
vdc    253:32   0 232.8G  0 disk
vdd    253:48   0 232.8G  0 disk
vde    253:64   0 232.8G  0 disk 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we have four disks available to be setup as RAID, instead of using mdadm based setup, we use zfs striped pool which is quite easy to setup&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;david$  sudo apt install zfsutils-linux
david$  sudo zpool create mysqlpool /dev/vdb /dev/vdc /dev/vdd /dev/vde 
We can also place some performance garnishes in ZFS to get slight improvements

# Disable access time updates
david$ sudo zfs set atime=off mysqlpool

# Set optimal record size for MySQL
david$ sudo zfs set recordsize=16K mysqlpool

# Cache only metadata to prevent double-caching
david$ sudo zfs set primarycache=metadata mysqlpool

# Enable LZ4 compression for better performance/storage ratio
david$ sudo zfs set compression=lz4 mysqlpool
Finally we can stop mysql server, move the directory /var/lib/mysql and mount new ZFS pool as new /var/lib/mysql

david$ sudo systemctl stop mysql
david$ sudo mv /var/lib/mysql /var/lib/mysql.old
david$ sudo zfs set mountpoint=/var/lib/mysql mysqlpool
david$ sudo rsync -av /var/lib/mysql.old/ /var/lib/mysql/
david$ sudo chown -R mysql:mysql /var/lib/mysql
david$ sudo systemctl restart mysql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After this massive exercise, looking much like entire cars being reassembled on a race track, we are back to running benchmark again. Anchor this that we have increased IOPS count 4x which is 20K now compared to 5K in default system. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvofyi2q1mv3d0cekzb88.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvofyi2q1mv3d0cekzb88.png" alt="Vanilla vs InnoDB Block Size vs MySQL Tune-up vs Striped RAID 0" width="800" height="637"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we again more than doubled ourselves of all key parameters bringing performance in stratospheric levels. This place is where "enterprise" databases live where core counts float in rivers of RAM. This performance levels are not available anything below $3000 in a cloud provider and the managed version being more expensive and serverless version even further expensive. &lt;/p&gt;

&lt;h2&gt;
  
  
  Benchmark overview
&lt;/h2&gt;

&lt;p&gt;We have improved our system in every key performance point for this benchmark and here is comparison in a snapshot. While this is great performance wise. We can also forgo some performance for higher resilience setup by using RAIDZ instead of Striped to increase resilience with same setup with marginal 18% loss of performance. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhshgovc8el1lwfq1lzvi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhshgovc8el1lwfq1lzvi.png" alt="Tabular Benchmark" width="800" height="761"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that we have these improvements how would we integrate it back to K8S setup. &lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: Kubernetes Setup with ZFS CSI Driver Method
&lt;/h2&gt;

&lt;p&gt;This approach uses a Container Storage Interface (CSI) driver to dynamically provision ZFS volumes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Install ZFS on Worker Nodes
&lt;/h3&gt;

&lt;p&gt;Apply the ZFS installation to each worker node that will host MySQL data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Run these commands on each designated worker node
sudo apt update
sudo apt install -y zfsutils-linux

# Create ZFS pool as before
sudo zpool create mysqlpool /dev/vdb /dev/vdc /dev/vdd /dev/vde

# Apply MySQL performance optimizations
sudo zfs set atime=off mysqlpool
sudo zfs set recordsize=16K mysqlpool
sudo zfs set primarycache=metadata mysqlpool
sudo zfs set compression=lz4 mysqlpool
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Label the Nodes with ZFS Storage
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ kubectl label nodes &amp;lt;node-name&amp;gt; storage=zfs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Install ZFS CSI Driver
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Clone the ZFS CSI driver repository
git clone https://github.com/democratic-csi/democratic-csi.git
cd democratic-csi

# Install using Helm
helm repo add democratic-csi https://democratic-csi.github.io/charts/
helm repo update

# Install the ZFS CSI driver
helm install zfs-csi democratic-csi/democratic-csi \
  --namespace kube-system \
  --create-namespace \
  -f ./examples/zfs-local-dataset.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create Storage Class for MySQL
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: zfs-mysql-sc
provisioner: org.democratic-csi.zfs
allowVolumeExpansion: true
parameters:
  fsType: zfs
  poolName: mysqlpool
  recordsize: "16k"
  primarycache: "metadata"
  compression: "lz4"
  atime: "off"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deploy MySQL as a StatefulSet
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
  namespace: database
spec:
  selector:
    matchLabels:
      app: mysql
  serviceName: mysql
  replicas: 1
  template:
    metadata:
      labels:
        app: mysql
    spec:
      nodeSelector:
        storage: zfs
      containers:
      - name: mysql
        image: mysql:8.0
        env:
        - name: MYSQL_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-secret
              key: password
        ports:
        - containerPort: 3306
          name: mysql
        volumeMounts:
        - name: data
          mountPath: /var/lib/mysql
        resources:
          requests:
            memory: "1Gi"
            cpu: "500m"
          limits:
            memory: "4Gi"
            cpu: "2000m"
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: ["ReadWriteOnce"]
      storageClassName: zfs-mysql-sc
      resources:
        requests:
          storage: 50Gi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create MySQL Service
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: v1
kind: Service
metadata:
  name: mysql
  namespace: database
spec:
  ports:
  - port: 3306
  selector:
    app: mysql
  clusterIP: None
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion: From Garage Sale Renault Clio to Enterprise Gold Prosche 917K
&lt;/h2&gt;

&lt;p&gt;Databases may be the last bastion of state in an increasingly stateless world—but that doesn’t mean they must also be the last bastion of uncontrolled cost. What we’ve demonstrated is a principled, low-budget approach to extract maximum performance per dollar by:&lt;/p&gt;

&lt;p&gt;Leveraging block-level tuning and file system optimizations.&lt;br&gt;
Exploiting MySQL configuration levers to align with system limits.&lt;br&gt;
Deploying ZFS RAID setups for high IOPS, with compression and smart caching.&lt;br&gt;
Seamlessly porting all these wins into Kubernetes using a CSI-driven ZFS architecture.&lt;/p&gt;

&lt;p&gt;At every step, we doubled performance, not by spending more, but by knowing more.&lt;/p&gt;

&lt;p&gt;What this shows is that infrastructure as craft—when applied with deliberate design and mechanical sympathy—can give you serverless-grade performance without serverless-grade cost.&lt;/p&gt;

&lt;p&gt;This isn’t just a benchmark hack—it’s a mindset shift.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.linkedin.com/pulse/snowflaking-get-3000-db-performance-138-supreet-sethi-n5zmc/" rel="noopener noreferrer"&gt;This material is also available at&lt;/a&gt;&lt;/p&gt;

</description>
      <category>mysql</category>
      <category>devops</category>
      <category>kubernetes</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
