<?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: Naim Katiman</title>
    <description>The latest articles on DEV Community by Naim Katiman (@naimkatiman).</description>
    <link>https://dev.to/naimkatiman</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%2F3686850%2F75804a2e-09e7-4de1-9142-169cff726e95.png</url>
      <title>DEV Community: Naim Katiman</title>
      <link>https://dev.to/naimkatiman</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/naimkatiman"/>
    <language>en</language>
    <item>
      <title>I Killed 3 Stub Functions in One Session</title>
      <dc:creator>Naim Katiman</dc:creator>
      <pubDate>Mon, 13 Apr 2026 01:24:48 +0000</pubDate>
      <link>https://dev.to/naimkatiman/i-killed-3-stub-functions-in-one-session-m58</link>
      <guid>https://dev.to/naimkatiman/i-killed-3-stub-functions-in-one-session-m58</guid>
      <description>&lt;p&gt;Every codebase has them. Stub functions that return hardcoded values, pretending to do real work.&lt;/p&gt;

&lt;p&gt;Yesterday I hunted down 3 of them in my Node.js project and replaced every single one with real implementations. Here's what changed.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Real CI Workflow Generation
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Before:&lt;/strong&gt; &lt;code&gt;createCIConfig()&lt;/code&gt; returned 0 lines. A stub. Nothing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;After:&lt;/strong&gt; Generates a full 22-line GitHub Actions workflow tailored to the project's stack — installs deps, runs tests, caches node_modules.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Auto Test Scaffolding
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Before:&lt;/strong&gt; &lt;code&gt;createTestScaffold()&lt;/code&gt; was a no-op.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;After:&lt;/strong&gt; Scans &lt;code&gt;src/&lt;/code&gt; for files missing test coverage and generates test file scaffolds automatically. No more guessing what needs tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Dynamic Summary Cards
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Before:&lt;/strong&gt; &lt;code&gt;generate-card.mjs&lt;/code&gt; had a hardcoded "Project Bootstrap" title.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;After:&lt;/strong&gt; Accepts dynamic summary text, change counts, and properly escapes XML. The card now reflects what actually shipped.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Scorecard
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Stubs killed&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tests passing&lt;/td&gt;
&lt;td&gt;21/21&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CI generated&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lines of real code&lt;/td&gt;
&lt;td&gt;~750&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  The Takeaway
&lt;/h2&gt;

&lt;p&gt;Stub functions are technical debt wearing a disguise. They pass CI. They don't throw errors. But they don't &lt;em&gt;do&lt;/em&gt; anything.&lt;/p&gt;

&lt;p&gt;If your codebase has functions that return empty arrays or hardcoded strings — go kill them. Today.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Building in public, one commit at a time.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>node</category>
      <category>opensource</category>
      <category>javascript</category>
      <category>ci</category>
    </item>
    <item>
      <title>Hardening an Express API: URL Validation, Error Handling, and Tests in One Session</title>
      <dc:creator>Naim Katiman</dc:creator>
      <pubDate>Sun, 12 Apr 2026 01:17:09 +0000</pubDate>
      <link>https://dev.to/naimkatiman/hardening-an-express-api-url-validation-error-handling-and-tests-in-one-session-54a6</link>
      <guid>https://dev.to/naimkatiman/hardening-an-express-api-url-validation-error-handling-and-tests-in-one-session-54a6</guid>
      <description>&lt;p&gt;I'm building &lt;a href="https://github.com/naimkatiman/repo-skill-advisor" rel="noopener noreferrer"&gt;repo-skill-advisor&lt;/a&gt;, a precision skill recommendation engine for GitHub repositories. Today I hardened the HTTP layer by closing three security and reliability gaps.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;The Express server accepted arbitrary URLs with no validation. Any string -- SSRF payloads, non-GitHub URLs, or empty strings -- was passed directly to the scanning engine. Error handling was inline with raw error messages leaking to clients.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  1. URL Validation Middleware
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;validateGitHubUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;urlStr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;urlStr&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;urlStr&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;u&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;urlStr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;protocol&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;protocol&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hostname&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;github.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;parts&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="na"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;parts&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;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;git$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&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;Applied as Express middleware to both POST routes so invalid URLs get rejected before reaching the scanner.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Centralized Error Handler
&lt;/h3&gt;

&lt;p&gt;Routes now call &lt;code&gt;next(err)&lt;/code&gt; and a single error handler catches everything -- no stack traces leak to clients.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. require.main Guards
&lt;/h3&gt;

&lt;p&gt;Both &lt;code&gt;server.js&lt;/code&gt; and &lt;code&gt;analyze.js&lt;/code&gt; now wrap CLI-only logic in &lt;code&gt;require.main === module&lt;/code&gt; checks, so they can be safely imported for testing without side effects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tests:&lt;/strong&gt; 39/39 passing (up from 33)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;New tests:&lt;/strong&gt; 6 validation tests covering valid URLs, non-GitHub URLs, missing repos, empty input, and protocol checks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security:&lt;/strong&gt; Zero SSRF vectors, no information leakage&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Key Takeaway
&lt;/h2&gt;

&lt;p&gt;Yesterday's automated analysis identified these exact gaps. Today I closed all three. Building systematic gap analysis into your workflow means you always know what to fix next.&lt;/p&gt;

&lt;p&gt;Check it out: &lt;a href="https://github.com/naimkatiman/repo-skill-advisor" rel="noopener noreferrer"&gt;repo-skill-advisor on GitHub&lt;/a&gt;&lt;/p&gt;

</description>
      <category>node</category>
      <category>security</category>
      <category>express</category>
      <category>opensource</category>
    </item>
    <item>
      <title>38.8% Win Rate, +21.83% P&amp;L: Why Being Wrong Most of the Time Still Prints Money</title>
      <dc:creator>Naim Katiman</dc:creator>
      <pubDate>Wed, 08 Apr 2026 04:54:14 +0000</pubDate>
      <link>https://dev.to/naimkatiman/388-win-rate-2183-pl-why-being-wrong-most-of-the-time-still-prints-money-3lk3</link>
      <guid>https://dev.to/naimkatiman/388-win-rate-2183-pl-why-being-wrong-most-of-the-time-still-prints-money-3lk3</guid>
      <description>&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%2Fk2emczql4g33i75em2p8.jpg" 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%2Fk2emczql4g33i75em2p8.jpg" alt=" " width="800" height="622"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;TradeClaw went last week. Here are the real numbers.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Numbers
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Total Signals&lt;/td&gt;
&lt;td&gt;354&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Resolved&lt;/td&gt;
&lt;td&gt;103&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Win Rate&lt;/td&gt;
&lt;td&gt;38.8%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Avg P&amp;amp;L per Signal&lt;/td&gt;
&lt;td&gt;+0.21%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Total P&amp;amp;L&lt;/td&gt;
&lt;td&gt;+21.83%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Starting Balance&lt;/td&gt;
&lt;td&gt;$10,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Current Balance&lt;/td&gt;
&lt;td&gt;$12,183&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Max Drawdown&lt;/td&gt;
&lt;td&gt;-10.66%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sharpe Ratio&lt;/td&gt;
&lt;td&gt;2.5&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;38.8% win rate. Looks bad on paper.&lt;/p&gt;

&lt;p&gt;+21.83% total P&amp;amp;L in 48 hours. Looks less bad.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Point
&lt;/h2&gt;

&lt;p&gt;Most people obsess over win rate. They want to be right. They want 70%, 80%, 90% accuracy.&lt;/p&gt;

&lt;p&gt;TradeClaw doesn't try to be right. It tries to be asymmetric.&lt;/p&gt;

&lt;p&gt;The system cuts losers fast and lets winners run. When it's wrong, it loses small. When it's right, it captures the full move. That math works even when you're wrong more than half the time.&lt;/p&gt;

&lt;p&gt;This is not a new idea. It's how every serious edge works. But most AI trading tools still optimize for accuracy instead of risk-adjusted returns. They chase win rate because it looks good in a demo. It doesn't survive real markets.&lt;/p&gt;

&lt;h2&gt;
  
  
  What TradeClaw Actually Does
&lt;/h2&gt;

&lt;p&gt;It's an open-source AI trading signal engine. Python. Free APIs. Free compute. No paid infrastructure.&lt;/p&gt;

&lt;p&gt;It scans crypto futures pairs on Binance (15m timeframe), detects three setup types:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Momentum Breakout&lt;/strong&gt; — volume spike + key level break&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mean Reversion&lt;/strong&gt; — liquidity grab / wick rejection at support/resistance&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trend Continuation&lt;/strong&gt; — pullback to EMA21 in a trending market&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every setup gets graded A+ through C. Only A+ and A setups get auto-executed on testnet. Every signal is tracked. Every outcome is recorded. The track record page at &lt;a href="https://tradeclaw.win" rel="noopener noreferrer"&gt;tradeclaw.win&lt;/a&gt; shows all of it — no cherry-picking.&lt;/p&gt;

&lt;h2&gt;
  
  
  Built With Zero Budget
&lt;/h2&gt;

&lt;p&gt;No paid APIs. No cloud servers. No subscription services.&lt;/p&gt;

&lt;p&gt;The entire stack runs on free resources. If you have Python and an internet connection, you can run it.&lt;/p&gt;

&lt;p&gt;This matters because most trading tools charge $50-200/month for signal access. TradeClaw gives you the signals, the logic, and the source code. You can verify every decision the system makes.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Need From You
&lt;/h2&gt;

&lt;p&gt;This is an open-source project and I'm building it in public. I need:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bug reports.&lt;/strong&gt; The system is 2 days old. Things will break. If you find something, &lt;a href="https://github.com/naimkatiman" rel="noopener noreferrer"&gt;open an issue&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ideas.&lt;/strong&gt; New setup detection patterns, better risk management logic, additional pairs, different timeframes — if you have a thesis, I want to hear it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code.&lt;/strong&gt; PRs are welcome. The codebase is Python, straightforward, and documented enough to jump into.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stars.&lt;/strong&gt; If the approach makes sense to you, star the repo. It helps with visibility and tells me the direction is worth pushing further.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;p&gt;Live track record: &lt;a href="https://tradeclaw.win" rel="noopener noreferrer"&gt;tradeclaw.win&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The win rate will probably stay under 50%. The P&amp;amp;L won't care.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Cut the losers. Let the winners run. That's the whole system.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>ai</category>
      <category>python</category>
    </item>
    <item>
      <title>I built an open-source AI trading signal platform — here's what I learned</title>
      <dc:creator>Naim Katiman</dc:creator>
      <pubDate>Mon, 30 Mar 2026 02:08:25 +0000</pubDate>
      <link>https://dev.to/naimkatiman/i-built-an-open-source-ai-trading-signal-platform-heres-what-i-learned-51dm</link>
      <guid>https://dev.to/naimkatiman/i-built-an-open-source-ai-trading-signal-platform-heres-what-i-learned-51dm</guid>
      <description>&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;Every time I wanted to trade forex or crypto, I had the same friction: open TradingView, look at RSI, look at MACD, look at EMA, try to decide if they agree, second-guess myself.&lt;/p&gt;

&lt;p&gt;The tools exist. The data exists. The math is solved. But nobody had packaged it as "here's the decision, not just the numbers."&lt;/p&gt;

&lt;p&gt;So I built &lt;a href="https://github.com/naimkatiman/tradeclaw" rel="noopener noreferrer"&gt;TradeClaw&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;p&gt;TradeClaw is a self-hosted AI trading signal platform. Point it at a symbol, and it returns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"direction"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"BUY"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"confidence"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;82&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"entry"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;94210.50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"stopLoss"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;93580.00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"takeProfit1"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;95420.00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"takeProfit2"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;96800.00&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not RSI=68. Not "MACD is crossing above signal line." A decision.&lt;/p&gt;

&lt;p&gt;It covers 10 assets across forex, crypto, and commodities. It runs entirely in Docker. It has a REST API, a Telegram bot, a paper trading simulator, and a backtest visualizer.&lt;/p&gt;

&lt;p&gt;It's MIT-licensed. Free forever. You own your data.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Next.js 14&lt;/strong&gt; — app router, server components&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TypeScript&lt;/strong&gt; — strict mode throughout
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No database&lt;/strong&gt; — file-based JSON (intentional, keeps deployment dead simple)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TA calculations&lt;/strong&gt; — pure TypeScript: RSI, MACD, EMA, Bollinger Bands, Stochastic, ATR, ADX&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data&lt;/strong&gt; — Binance API (crypto), Yahoo Finance (forex/commodities)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docker&lt;/strong&gt; — single &lt;code&gt;docker compose up -d&lt;/code&gt; and it works&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The signal engine
&lt;/h2&gt;

&lt;p&gt;This was the interesting part. Most indicator libraries just return numbers. I needed decisions.&lt;/p&gt;

&lt;p&gt;The approach: &lt;strong&gt;weighted scoring&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Simplified version&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;scoreIndicators&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;indicators&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AllIndicators&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;candles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;OHLCV&lt;/span&gt;&lt;span class="p"&gt;[]):&lt;/span&gt; &lt;span class="nx"&gt;SignalScore&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;buyScore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;sellScore&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="c1"&gt;// RSI&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;indicators&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rsi&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;35&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;buyScore&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;indicators&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rsi&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;65&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;sellScore&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// MACD histogram direction&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;indicators&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;macd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;histogram&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="nx"&gt;buyScore&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="k"&gt;else&lt;/span&gt; &lt;span class="nx"&gt;sellScore&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="c1"&gt;// EMA crossover&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;indicators&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ema20&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;indicators&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ema50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;buyScore&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;else&lt;/span&gt; &lt;span class="nx"&gt;sellScore&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Bollinger Band position&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;price&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;indicators&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lower&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;1.01&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;buyScore&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="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;price&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;indicators&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;upper&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.99&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;sellScore&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="c1"&gt;// ... more indicators&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;totalScore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;buyScore&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;sellScore&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;confidence&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buyScore&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sellScore&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;totalScore&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;direction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;buyScore&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;sellScore&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;BUY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SELL&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;confidence&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;buyScore&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;sellScore&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;The full version (&lt;code&gt;apps/web/app/lib/signal-generator.ts&lt;/code&gt;) adds:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Market quality gating (ATR%, Bollinger bandwidth, EMA trend strength)&lt;/li&gt;
&lt;li&gt;Stochastic confirmation&lt;/li&gt;
&lt;li&gt;Multi-timeframe confluence (+15% confidence if H1/H4/D1 all agree)&lt;/li&gt;
&lt;li&gt;Support/resistance proximity for stop placement&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What I learned building this
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Synthetic fallback is essential
&lt;/h3&gt;

&lt;p&gt;Real trading APIs (Binance, Yahoo Finance) fail. Rate limits, network issues, outages. I built a synthetic fallback that generates realistic OHLCV data deterministically, so the platform always works even without internet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// When live APIs fail, use seeded pseudo-random price simulation&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;generateSyntheticCandles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;OHLCV&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;BASE_PRICES&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;seed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;hashSymbol&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="p"&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="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;seed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;seed&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1664525&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1013904223&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mh"&gt;0xffffffff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;change&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;seed&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;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="o"&gt;/&lt;/span&gt; &lt;span class="mh"&gt;0xffffffff&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.004&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// ... generate realistic OHLCV&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;
  
  
  2. No-database architecture is underrated
&lt;/h3&gt;

&lt;p&gt;My first instinct was PostgreSQL. Instead, I used JSON files for everything — signal history, webhook configs, API keys, paper trading positions, price alerts.&lt;/p&gt;

&lt;p&gt;Result:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Zero setup for self-hosting&lt;/li&gt;
&lt;li&gt;Instant backup (just copy the data/ folder)&lt;/li&gt;
&lt;li&gt;No connection pooling issues&lt;/li&gt;
&lt;li&gt;Works in Vercel Edge functions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The downside: concurrent writes need locking. I used a simple in-memory queue. Good enough for a single-user self-hosted tool.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. The plugin system was worth building
&lt;/h3&gt;

&lt;p&gt;I added a plugin system where users can write custom JavaScript indicators:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Custom VWAP indicator&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;compute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;candles&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;cumPV&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cumVol&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;candles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;typical&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;high&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;low&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;close&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;cumPV&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;typical&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;volume&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;cumVol&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;volume&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="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cumPV&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;cumVol&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;Each plugin runs in a sandboxed &lt;code&gt;Function()&lt;/code&gt; with mock candles for validation. Community plugins can be shared as JSON.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. MCP is the new RSS
&lt;/h3&gt;

&lt;p&gt;Just shipped a Model Context Protocol server:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Add this to Claude Desktop, and you can ask "What's the current BTC signal?" and get a full indicator breakdown. This is genuinely useful.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Viral signals from &lt;code&gt;npx&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The fastest way to get developers to try something is &lt;code&gt;npx &amp;lt;package&amp;gt;&lt;/code&gt;. No install, no config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx tradeclaw signals &lt;span class="nt"&gt;--pair&lt;/span&gt; BTCUSD
npx tradeclaw-demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The demo command spins up a local Express server with Bloomberg-style UI and a live SSE signal stream. All in under 3 seconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  The honest part
&lt;/h2&gt;

&lt;p&gt;Does following these signals make money? I don't know yet.&lt;/p&gt;

&lt;p&gt;The signal accuracy pages currently use seed data for demonstration. I've built the infrastructure to track real emitted signals and compute audited outcomes — but that needs months of live data to be meaningful.&lt;/p&gt;

&lt;p&gt;I'm not claiming this prints money. I'm claiming it's a well-built, transparent, self-hostable tool for exploring algorithmic signal generation. The math is open source. You can audit every line.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;MT4/MT5 broker connectivity (actually send signals to your broker)&lt;/li&gt;
&lt;li&gt;Machine learning confidence model (trained on tracked signal outcomes)&lt;/li&gt;
&lt;li&gt;More assets (stocks, indices, energy)&lt;/li&gt;
&lt;/ul&gt;

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



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Docker (recommended)&lt;/span&gt;
git clone https://github.com/naimkatiman/tradeclaw
&lt;span class="nb"&gt;cd &lt;/span&gt;tradeclaw &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;

&lt;span class="c"&gt;# Or just demo it&lt;/span&gt;
npx tradeclaw-demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Live demo: &lt;a href="https://tradeclaw.win" rel="noopener noreferrer"&gt;tradeclaw.win&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;GitHub: &lt;a href="https://github.com/naimkatiman/tradeclaw" rel="noopener noreferrer"&gt;naimkatiman/tradeclaw&lt;/a&gt; — stars help a lot 🌟&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built with Next.js, TypeScript, and a lot of caffeine. Feedback welcome in the GitHub Discussions.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>trading</category>
      <category>nextjs</category>
      <category>typescript</category>
    </item>
    <item>
      <title>I Built an Open-Source Alternative to $500/Year Trading Signal Tools</title>
      <dc:creator>Naim Katiman</dc:creator>
      <pubDate>Sat, 28 Mar 2026 17:49:55 +0000</pubDate>
      <link>https://dev.to/naimkatiman/i-built-an-open-source-alternative-to-500year-trading-signal-tools-4lg1</link>
      <guid>https://dev.to/naimkatiman/i-built-an-open-source-alternative-to-500year-trading-signal-tools-4lg1</guid>
      <description>&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%2Feotbahevq5dambc0fj7p.gif" 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%2Feotbahevq5dambc0fj7p.gif" alt=" " width="760" height="427"&gt;&lt;/a&gt;&lt;br&gt;
I was spending $47/month on TradingView Pro + 3Commas to get decent trading signals for XAUUSD, BTCUSD, and EURUSD. After a year, I cancelled both and built my own.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TradeClaw&lt;/strong&gt; is now open source: &lt;a href="https://github.com/naimkatiman/tradeclaw" rel="noopener noreferrer"&gt;https://github.com/naimkatiman/tradeclaw&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Real-time buy/sell signals for 12 assets (XAUUSD, BTCUSD, ETHUSD, EURUSD, GBPUSD, and more)&lt;/li&gt;
&lt;li&gt;Signal engine: weighted RSI (28%), MACD (20%), EMA crossovers (20%), Support/Resistance (20%), Bollinger Bands (6%), Stochastic (6%)&lt;/li&gt;
&lt;li&gt;Backtesting against historical data&lt;/li&gt;
&lt;li&gt;Telegram alerts when signals fire&lt;/li&gt;
&lt;li&gt;Beautiful dark dashboard with candlestick charts&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The cost comparison
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Monthly&lt;/th&gt;
&lt;th&gt;Annual&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;TradingView Pro&lt;/td&gt;
&lt;td&gt;$15&lt;/td&gt;
&lt;td&gt;$180&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3Commas Starter&lt;/td&gt;
&lt;td&gt;$29&lt;/td&gt;
&lt;td&gt;$348&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TradeClaw (self-hosted)&lt;/td&gt;
&lt;td&gt;$5 VPS&lt;/td&gt;
&lt;td&gt;$60&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Deploy in 60 seconds
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/naimkatiman/tradeclaw
&lt;span class="nb"&gt;cd &lt;/span&gt;tradeclaw
&lt;span class="nb"&gt;cp&lt;/span&gt; .env.example .env
docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt;. That's it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why open source?
&lt;/h2&gt;

&lt;p&gt;These tools shouldn't cost $500/year. The technical analysis is well-understood math. The value isn't in the algorithm — it's in the UX, the integrations, and reliability.&lt;/p&gt;

&lt;p&gt;TradeClaw is MIT licensed. Self-host it, modify it, contribute back.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Live demo:&lt;/strong&gt; &lt;a href="https://tradeclaw.win/demo" rel="noopener noreferrer"&gt;https://tradeclaw.win/demo&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/naimkatiman/tradeclaw" rel="noopener noreferrer"&gt;https://github.com/naimkatiman/tradeclaw&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;⭐ Star it if you find it useful — it helps others discover the project.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>trading</category>
      <category>selfhosted</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
