backtest-kit-docs / documents /article_03_claude_trader.html
tripolskypetr's picture
patch
d99335f
Raw
History Blame Contribute Delete
39.7 kB
<!DOCTYPE html><html class="default" lang="en" data-base=".."><head><meta charset="utf-8"/><meta http-equiv="x-ua-compatible" content="IE=edge"/><title>article/03_claude_trader | backtest-kit</title><meta name="description" content="Documentation for backtest-kit"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="../assets/style.css"/><link rel="stylesheet" href="../assets/highlight.css"/><script defer src="../assets/main.js"></script><script async src="../assets/icons.js" id="tsd-icons-script"></script><script async src="../assets/search.js" id="tsd-search-script"></script><script async src="../assets/navigation.js" id="tsd-nav-script"></script><script async src="../assets/hierarchy.js" id="tsd-hierarchy-script"></script></head><body><script>document.documentElement.dataset.theme = localStorage.getItem("tsd-theme") || "os";document.body.style.display="none";setTimeout(() => app?app.showPage():document.body.style.removeProperty("display"),500)</script><header class="tsd-page-toolbar"><div class="tsd-toolbar-contents container"><div class="table-cell" id="tsd-search"><div class="field"><label for="tsd-search-field" class="tsd-widget tsd-toolbar-icon search no-caption"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-search"></use></svg></label><input type="text" id="tsd-search-field" aria-label="Search"/></div><div class="field"><div id="tsd-toolbar-links"></div></div><ul class="results"><li class="state loading">Preparing search index...</li><li class="state failure">The search index is not available</li></ul><a href="../index.html" class="title">backtest-kit</a></div><div class="table-cell" id="tsd-widgets"><a href="#" class="tsd-widget tsd-toolbar-icon menu no-caption" data-toggle="menu" aria-label="Menu"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-menu"></use></svg></a></div></div></header><div class="container container-main"><div class="col-content"><div class="tsd-page-title"><ul class="tsd-breadcrumb"><li><a href="../modules.html">backtest-kit</a></li><li><a href="article_03_claude_trader.html">article/03_claude_trader</a></li></ul></div><div class="tsd-panel tsd-typography"><a id="๐Ÿ“Š-how-ai-got-hands-for-stock-trading" class="tsd-anchor"></a><h1 class="tsd-anchor-link">๐Ÿ“Š How AI Got Hands for Stock Trading<a href="#๐Ÿ“Š-how-ai-got-hands-for-stock-trading" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h1><blockquote>
<p>The source code discussed in the article is <a href="https://github.com/tripolskypetr/backtest-kit">available at this link</a></p>
</blockquote>
<p>In paintings or heraldry, Saint George is always depicted with a spear. But why a spear? You could depict a bow, sword, or mace. The long shaft of the spear reaches up into the clouds to the heavens (<s>the lord</s>): knowledge and power work with an instrument of action like left and right legs.</p>
<p><img src="../media/saint_george.png" alt="The metaphor with Claude Code is straightforward. Impossible? Possible."></p>
<p>This is exactly the problem with Claude Code.</p>
<p>Claude Code is an AI agent from Anthropic for autonomous code development. It possesses superhuman pattern recognition, can analyze 50MB of backtest logs in seconds (by inventing and debugging a Python script through test-driven development), determine why 80% of trades close at timeout or SL, and modify Pine Script indicators with surgical precision.</p>
<p>But it has no hands.</p>
<a id="the-tradingview-problem-the-human-becomes-the-bottleneck" class="tsd-anchor"></a><h2 class="tsd-anchor-link">The TradingView Problem: The Human Becomes the Bottleneck<a href="#the-tradingview-problem-the-human-becomes-the-bottleneck" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2><p>If we discard the marketing, the real process of creating an automated production trading strategy looks like this. TradingView replaced MetaTrader, trading strategies are written on it.</p>
<ol>
<li>Write a strategy in the Pine Script web editor on the TradingView website.</li>
<li>Run a backtest (limit ~5000 bars if you don't have a bank card).</li>
<li>Download CSV.</li>
<li>Send to Claude/Grok/DeepSeek</li>
<li>Change one line.</li>
<li>Repeat.</li>
</ol>
<p><strong>The iteration cost is too high due to clumsy human hands</strong>: the number of CSV files either turns the downloads folder into a mess, or running a week's worth of backtest time takes a day of thoughtful work because you need to name and organize files into folders. A human physically can't work that long.</p>
<p>And you can also send the wrong file to the chat and catch look-ahead bias.</p>
<a id="the-architectural-flaw-of-pine-script" class="tsd-anchor"></a><h2 class="tsd-anchor-link">The Architectural Flaw of Pine Script<a href="#the-architectural-flaw-of-pine-script" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2><p>The Pine Script language is syntactic sugar for a for loop where variables are declared both inside and outside the loop block.</p>
<pre><code class="javascript"><span class="hl-0">// Local in the candle iteration loop</span><br/><span class="hl-5">long_cond</span><span class="hl-2"> = </span><span class="hl-5">ta</span><span class="hl-2">.</span><span class="hl-1">crossover</span><span class="hl-2">(</span><span class="hl-5">ema_fast</span><span class="hl-2">, </span><span class="hl-5">ema_slow</span><span class="hl-2">) </span><span class="hl-5">and</span><span class="hl-2"> </span><span class="hl-5">rsi</span><span class="hl-2"> &gt; </span><span class="hl-8">40</span><span class="hl-2"> </span><span class="hl-5">and</span><span class="hl-2"> </span><span class="hl-5">rsi</span><span class="hl-2"> &lt; </span><span class="hl-8">65</span><span class="hl-2"> </span><span class="hl-5">and</span><span class="hl-2"> </span><span class="hl-5">trend_up</span><span class="hl-2"> </span><span class="hl-5">and</span><span class="hl-2"> </span><span class="hl-5">vol_spike</span><span class="hl-2"> </span><span class="hl-5">and</span><span class="hl-2"> </span><span class="hl-5">mom_up</span><span class="hl-2"> </span><br/><span class="hl-5">short_cond</span><span class="hl-2"> = </span><span class="hl-5">ta</span><span class="hl-2">.</span><span class="hl-1">crossunder</span><span class="hl-2">(</span><span class="hl-5">ema_fast</span><span class="hl-2">, </span><span class="hl-5">ema_slow</span><span class="hl-2">) </span><span class="hl-5">and</span><span class="hl-2"> </span><span class="hl-5">rsi</span><span class="hl-2"> &lt; </span><span class="hl-8">60</span><span class="hl-2"> </span><span class="hl-5">and</span><span class="hl-2"> </span><span class="hl-5">rsi</span><span class="hl-2"> &gt; </span><span class="hl-8">35</span><span class="hl-2"> </span><span class="hl-5">and</span><span class="hl-2"> </span><span class="hl-5">trend_down</span><span class="hl-2"> </span><span class="hl-5">and</span><span class="hl-2"> </span><span class="hl-5">vol_spike</span><span class="hl-2"> </span><span class="hl-5">and</span><span class="hl-2"> </span><span class="hl-5">mom_down</span><br/><br/><span class="hl-0">// Global outside the loop</span><br/><span class="hl-4">var</span><span class="hl-2"> </span><span class="hl-5">int</span><span class="hl-2"> bars_since_signal = </span><span class="hl-8">0</span>
</code><button type="button">Copy</button></pre>
<p>As a consequence, if you need to wait for favorable conditions on both fast (15m) and long (4h) timeframes, you get workarounds:</p>
<pre><code class="javascript"><span class="hl-5">ema200_4h</span><span class="hl-2"> = </span><span class="hl-5">request</span><span class="hl-2">.</span><span class="hl-1">security</span><span class="hl-2">(</span><br/><span class="hl-2"> </span><span class="hl-5">syminfo</span><span class="hl-2">.</span><span class="hl-5">tickerid</span><span class="hl-2">,</span><br/><span class="hl-2"> </span><span class="hl-3">&quot;240&quot;</span><span class="hl-2">,</span><br/><span class="hl-2"> </span><span class="hl-5">ta</span><span class="hl-2">.</span><span class="hl-1">ema</span><span class="hl-2">(</span><span class="hl-5">close</span><span class="hl-2">, </span><span class="hl-8">200</span><span class="hl-2">),</span><br/><span class="hl-2"> </span><span class="hl-5">lookahead</span><span class="hl-2"> = </span><span class="hl-5">barmerge</span><span class="hl-2">.</span><span class="hl-5">lookahead_off</span><br/><span class="hl-2">)</span>
</code><button type="button">Copy</button></pre>
<p>The closest analogy to this code from mathematics is a complex number. The number itself means nothing: you need the dynamics of change in a series of numbers (whether it's generally growing or falling). And double for loops are inefficient (quadratic complexity), so they weren't implemented. And it's also unclear what <code>barmerge.lookahead_off</code> does.</p>
<pre><code class="javascript"><span class="hl-0">// Is this correct?</span><br/><span class="hl-5">breakeven</span><span class="hl-2"> = </span><span class="hl-5">entryPrice</span><span class="hl-2"> * (</span><span class="hl-8">1</span><span class="hl-2"> + </span><span class="hl-5">commissionPct</span><span class="hl-2"> * </span><span class="hl-8">2</span><span class="hl-2"> + </span><span class="hl-5">slippagePct</span><span class="hl-2"> * </span><span class="hl-8">2</span><span class="hl-2">)</span><br/><br/><span class="hl-0">// Or this?</span><br/><span class="hl-5">breakeven</span><span class="hl-2"> = </span><span class="hl-5">entryPrice</span><span class="hl-2"> * (</span><span class="hl-8">1</span><span class="hl-2"> + </span><span class="hl-5">entryFee</span><span class="hl-2">) / (</span><span class="hl-8">1</span><span class="hl-2"> - </span><span class="hl-5">exitFee</span><span class="hl-2"> - </span><span class="hl-5">slippage</span><span class="hl-2">)</span><br/><br/><span class="hl-0">// Or maybe this?</span><br/><span class="hl-9">trueBreakeven</span><span class="hl-2"> := </span><span class="hl-5">entryPrice</span><span class="hl-2"> * (</span><span class="hl-8">1</span><span class="hl-2"> + </span><span class="hl-5">entryCommission</span><span class="hl-2">) / (</span><span class="hl-8">1</span><span class="hl-2"> - </span><span class="hl-5">exitCommission</span><span class="hl-2"> - </span><span class="hl-5">slippage</span><span class="hl-2">) * (</span><span class="hl-8">1</span><span class="hl-2"> + </span><span class="hl-5">fundingRate</span><span class="hl-2"> * </span><span class="hl-5">holdingPeriod</span><span class="hl-2"> / </span><span class="hl-8">8</span><span class="hl-2">)</span>
</code><button type="button">Copy</button></pre>
<p>Things are even more fun with breakeven math: this code is unreadable, any language model hallucination will indistinguishably create a loss. Meanwhile, the programmer will be convinced that the problem is in the indicators.</p>
<a id="and-now-it-has-hands" class="tsd-anchor"></a><h2 class="tsd-anchor-link">And Now It Has Hands<a href="#and-now-it-has-hands" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2><p>To eliminate the human factor, you need to create a system that will:</p>
<ol>
<li>
<p><strong>Run Pine Script locally self-hosted</strong></p>
<p>Already done at <a href="https://github.com/QuantForgeOrg/PineTS">https://github.com/QuantForgeOrg/PineTS</a>. Indicator visualization is already open source, trading strategies are not interesting within the scope of the article.</p>
</li>
<li>
<p><strong>Ensure time synchronization outside of LLM-editable code</strong></p>
<p>Covered in the article <a href="article_01_look_ahead_bias.html">Look ahead Bias</a>. It's important to move critical code out of the AI agent's working zone to avoid sloppy work or hallucinations.</p>
</li>
<li>
<p><strong>Same code in backtest/live/paper</strong></p>
<p>Covered in the article <a href="article_02_second_order_chaos.html">Second order chaos</a>. It talks about trading strategies in natural language, but seamless live/paper/dev migration is already done.</p>
</li>
<li>
<p><strong>Maintain structured JSONL logs as markup native to LLM agents</strong></p>
<p>JSONL (JSON Lines) is a format for both <a href="https://huggingface.co/blog/cfahlgren1/intro-to-parquet-format">datasets in huggingface</a> and for logs. Claude reads them with an extended limit or writes a script itself for quick line search.</p>
</li>
<li>
<p><strong>Agent Skills file to avoid repeated research on a new chat context</strong></p>
<p>Created automatically when running the command <code>npx -y @backtest-kit/sidekick</code>. This also allows you to ask regular Claude chat to use backtest-kit code for research by creating a docker container on the server side without touching the programmer's machine.</p>
</li>
</ol>
<p>Additionally, since everything is open source, this can be run on any exchange, including those not available in TradingView or where the company has disabled the broker.</p>
<a id="fixing-pine-script" class="tsd-anchor"></a><h2 class="tsd-anchor-link">Fixing Pine Script<a href="#fixing-pine-script" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2><p>To combine data from different timeframes, you need to run two separate indicators simultaneously. The complete program code <a href="https://github.com/tripolskypetr/backtest-kit">at the link</a> already contains a caching mechanism for the higher 4h timeframe, calculations are optimized.</p>
<pre><code class="typescript"><span class="hl-6">import</span><span class="hl-2"> { </span><span class="hl-5">addStrategySchema</span><span class="hl-2"> } </span><span class="hl-6">from</span><span class="hl-2"> </span><span class="hl-3">&quot;backtest-kit&quot;</span><span class="hl-2">;</span><br/><span class="hl-6">import</span><span class="hl-2"> { </span><span class="hl-5">randomString</span><span class="hl-2"> } </span><span class="hl-6">from</span><span class="hl-2"> </span><span class="hl-3">&quot;functools-kit&quot;</span><span class="hl-2">;</span><br/><br/><span class="hl-6">import</span><span class="hl-2"> </span><span class="hl-4">*</span><span class="hl-2"> </span><span class="hl-6">as</span><span class="hl-2"> </span><span class="hl-5">math_15m</span><span class="hl-2"> </span><span class="hl-6">from</span><span class="hl-2"> </span><span class="hl-3">&quot;../../math/timeframe_15m.math.mjs&quot;</span><span class="hl-2">;</span><br/><span class="hl-6">import</span><span class="hl-2"> </span><span class="hl-4">*</span><span class="hl-2"> </span><span class="hl-6">as</span><span class="hl-2"> </span><span class="hl-5">math_4h</span><span class="hl-2"> </span><span class="hl-6">from</span><span class="hl-2"> </span><span class="hl-3">&quot;../../math/timeframe_4h.math.mjs&quot;</span><span class="hl-2">;</span><br/><br/><span class="hl-6">import</span><span class="hl-2"> </span><span class="hl-5">StrategyName</span><span class="hl-2"> </span><span class="hl-6">from</span><span class="hl-2"> </span><span class="hl-3">&quot;../../enum/StrategyName.mjs&quot;</span><span class="hl-2">;</span><br/><span class="hl-6">import</span><span class="hl-2"> </span><span class="hl-5">RiskName</span><span class="hl-2"> </span><span class="hl-6">from</span><span class="hl-2"> </span><span class="hl-3">&quot;../../enum/RiskName.mjs&quot;</span><span class="hl-2">;</span><br/><br/><span class="hl-1">addStrategySchema</span><span class="hl-2">({</span><br/><span class="hl-2"> </span><span class="hl-5">strategyName:</span><span class="hl-2"> </span><span class="hl-5">StrategyName</span><span class="hl-2">.</span><span class="hl-5">MainStrategy</span><span class="hl-2">,</span><br/><span class="hl-2"> </span><span class="hl-5">interval:</span><span class="hl-2"> </span><span class="hl-3">&quot;5m&quot;</span><span class="hl-2">,</span><br/><span class="hl-2"> </span><span class="hl-1">getSignal</span><span class="hl-5">:</span><span class="hl-2"> </span><span class="hl-4">async</span><span class="hl-2"> (</span><span class="hl-5">symbol</span><span class="hl-2">) </span><span class="hl-4">=&gt;</span><span class="hl-2"> {</span><br/><br/><span class="hl-2"> </span><span class="hl-4">const</span><span class="hl-2"> </span><span class="hl-7">signalId</span><span class="hl-2"> = </span><span class="hl-1">randomString</span><span class="hl-2">();</span><br/><span class="hl-2"> </span><br/><span class="hl-2"> </span><span class="hl-4">const</span><span class="hl-2"> </span><span class="hl-7">data_4h</span><span class="hl-2"> = </span><span class="hl-6">await</span><span class="hl-2"> </span><span class="hl-5">math_4h</span><span class="hl-2">.</span><span class="hl-1">getData</span><span class="hl-2">(</span><span class="hl-5">signalId</span><span class="hl-2">, </span><span class="hl-5">symbol</span><span class="hl-2">);</span><br/><span class="hl-2"> </span><span class="hl-0">// await pinescript.run(</span><br/><span class="hl-2"> </span><span class="hl-0">// File.fromPath(&quot;timeframe_4h.pine&quot;), </span><br/><span class="hl-2"> </span><span class="hl-0">// { symbol, timeframe: &quot;4h&quot;, limit: 100 }</span><br/><span class="hl-2"> </span><span class="hl-0">// )</span><br/><br/><span class="hl-2"> </span><span class="hl-6">if</span><span class="hl-2"> (</span><span class="hl-5">data_4h</span><span class="hl-2">.</span><span class="hl-5">noTrades</span><span class="hl-2">) {</span><br/><span class="hl-2"> </span><span class="hl-6">return</span><span class="hl-2"> </span><span class="hl-4">null</span><span class="hl-2">;</span><br/><span class="hl-2"> }</span><br/><br/><span class="hl-2"> </span><span class="hl-4">const</span><span class="hl-2"> </span><span class="hl-7">data_15m</span><span class="hl-2"> = </span><span class="hl-6">await</span><span class="hl-2"> </span><span class="hl-5">math_15m</span><span class="hl-2">.</span><span class="hl-1">getData</span><span class="hl-2">(</span><span class="hl-5">signalId</span><span class="hl-2">, </span><span class="hl-5">symbol</span><span class="hl-2">);</span><br/><span class="hl-2"> </span><span class="hl-0">// await run(</span><br/><span class="hl-2"> </span><span class="hl-0">// File.fromPath(&quot;timeframe_15m.pine&quot;), </span><br/><span class="hl-2"> </span><span class="hl-0">// { symbol, timeframe: &quot;15m&quot;, limit: 100 }</span><br/><span class="hl-2"> </span><span class="hl-0">// )</span><br/><br/><span class="hl-2"> </span><span class="hl-6">if</span><span class="hl-2"> (</span><span class="hl-5">data_15m</span><span class="hl-2">.</span><span class="hl-5">position</span><span class="hl-2"> === </span><span class="hl-8">0</span><span class="hl-2">) {</span><br/><span class="hl-2"> </span><span class="hl-6">return</span><span class="hl-2"> </span><span class="hl-4">null</span><span class="hl-2">;</span><br/><span class="hl-2"> }</span><br/><br/><span class="hl-2"> </span><span class="hl-6">if</span><span class="hl-2"> (</span><span class="hl-5">data_4h</span><span class="hl-2">.</span><span class="hl-5">allowShort</span><span class="hl-2"> &amp;&amp; </span><span class="hl-5">data_15m</span><span class="hl-2">.</span><span class="hl-5">position</span><span class="hl-2"> === </span><span class="hl-8">1</span><span class="hl-2">) {</span><br/><span class="hl-2"> </span><span class="hl-6">return</span><span class="hl-2"> </span><span class="hl-4">null</span><span class="hl-2">;</span><br/><span class="hl-2"> }</span><br/><br/><span class="hl-2"> </span><span class="hl-6">if</span><span class="hl-2"> (</span><span class="hl-5">data_4h</span><span class="hl-2">.</span><span class="hl-5">allowLong</span><span class="hl-2"> &amp;&amp; </span><span class="hl-5">data_15m</span><span class="hl-2">.</span><span class="hl-5">position</span><span class="hl-2"> === -</span><span class="hl-8">1</span><span class="hl-2">) {</span><br/><span class="hl-2"> </span><span class="hl-6">return</span><span class="hl-2"> </span><span class="hl-4">null</span><span class="hl-2">;</span><br/><span class="hl-2"> }</span><br/><br/><span class="hl-2"> {</span><br/><span class="hl-2"> </span><span class="hl-5">math_15m</span><span class="hl-2">.</span><span class="hl-1">dumpPlot</span><span class="hl-2">(</span><span class="hl-5">signalId</span><span class="hl-2">, </span><span class="hl-5">symbol</span><span class="hl-2">);</span><br/><span class="hl-2"> </span><span class="hl-5">math_4h</span><span class="hl-2">.</span><span class="hl-1">dumpPlot</span><span class="hl-2">(</span><span class="hl-5">signalId</span><span class="hl-2">, </span><span class="hl-5">symbol</span><span class="hl-2">);</span><br/><span class="hl-2"> }</span><br/><br/><span class="hl-2"> </span><span class="hl-6">return</span><span class="hl-2"> </span><span class="hl-6">await</span><span class="hl-2"> </span><span class="hl-5">math_15m</span><span class="hl-2">.</span><span class="hl-1">getSignal</span><span class="hl-2">(</span><span class="hl-5">signalId</span><span class="hl-2">, </span><span class="hl-5">symbol</span><span class="hl-2">);</span><br/><span class="hl-2"> },</span><br/><span class="hl-2"> </span><span class="hl-5">riskList:</span><span class="hl-2"> [</span><br/><span class="hl-2"> </span><span class="hl-5">RiskName</span><span class="hl-2">.</span><span class="hl-5">TakeProfitDistanceRisk</span><span class="hl-2">, </span><br/><span class="hl-2"> </span><span class="hl-5">RiskName</span><span class="hl-2">.</span><span class="hl-5">StopLossDistanceRisk</span><br/><span class="hl-2"> ],</span><br/><span class="hl-2">});</span>
</code><button type="button">Copy</button></pre>
<a id="how-claude-code-uses-this" class="tsd-anchor"></a><h2 class="tsd-anchor-link">How Claude Code Uses This<a href="#how-claude-code-uses-this" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2><blockquote>
<p>I won't be able to explain briefly. In the images you can see an example of exiting a position using Kelly: the price didn't reach TP, but a local spike (lower left corner) allowed earning 90% profit through partial exits on the 15-minute timeframe, despite the fact that the price then oscillated sideways (lower right corner) and on the hourly timeframe began to fall.</p>
</blockquote>
<p><img src="../media/screenshot10.png" alt="Claude configures this itself based on JSONL logs"></p>
<p>Not only technical, but also multi-timeframe and fundamental analysis is available. Anthropic has already created and maintains internet search tools for Claude, so it will find news on the internet itself and, if necessary, will only open short positions. Also, the <code>@backtest-kit/ollama</code> package and the async nature of the <code>getSignal</code> function allow searching for news and forecasts on the internet on the fly without changing the bot code. But the combination of <a href="https://code.claude.com/docs/en/claude-code-on-the-web">Claude Code on the web</a> plus <a href="https://docs.digitalocean.com/products/app-platform/how-to/deploy-from-github-actions/">GitHub Actions</a> is significantly more convenient: you can give a command to adapt the strategy from an iPad while on vacation.</p>
<a id="example-of-creating-a-trading-bot" class="tsd-anchor"></a><h2 class="tsd-anchor-link">Example of Creating a Trading Bot<a href="#example-of-creating-a-trading-bot" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2><p><em><strong>Human</strong></em>: <em>Create a crypto trading bot using RSI divergence and volume confirmation</em></p>
<p><em><strong>Claude Code</strong></em>:</p>
<pre><code class="bash"><span class="hl-1">npx</span><span class="hl-2"> </span><span class="hl-3">@backtest-kit/sidekick</span><span class="hl-2"> </span><span class="hl-3">crypto-bot</span><br/><span class="hl-1">cd</span><span class="hl-2"> </span><span class="hl-3">crypto-bot</span><br/><br/><span class="hl-0"># Reads generated Pine Script templates</span><br/><span class="hl-1">cat</span><span class="hl-2"> </span><span class="hl-3">config/source/timeframe_15m.pine</span>
</code><button type="button">Copy</button></pre>
<p><em><strong>Claude Code</strong></em>: <em>I see a template with EMA crossover. Modifying it for RSI divergence...</em></p>
<pre><code class="javascript"><span class="hl-2"># </span><span class="hl-5">Edits</span><span class="hl-2"> </span><span class="hl-5">Pine</span><span class="hl-2"> </span><span class="hl-5">Script</span><span class="hl-2"> </span><span class="hl-5">strategy</span><br/><span class="hl-5">cat</span><span class="hl-2"> &gt; </span><span class="hl-5">config</span><span class="hl-2">/</span><span class="hl-5">source</span><span class="hl-2">/</span><span class="hl-5">rsi_divergence</span><span class="hl-2">.</span><span class="hl-5">pine</span><span class="hl-2"> &lt;&lt; </span><span class="hl-3">&#39;EOF&#39;</span><br/><span class="hl-0">//@version=5</span><br/><span class="hl-1">indicator</span><span class="hl-2">(</span><span class="hl-3">&quot;RSI Divergence Strategy&quot;</span><span class="hl-2">)</span><br/><br/><span class="hl-0">// RSI calculation</span><br/><span class="hl-5">rsi</span><span class="hl-2"> = </span><span class="hl-5">ta</span><span class="hl-2">.</span><span class="hl-1">rsi</span><span class="hl-2">(</span><span class="hl-5">close</span><span class="hl-2">, </span><span class="hl-8">14</span><span class="hl-2">)</span><br/><span class="hl-5">rsi_prev</span><span class="hl-2"> = </span><span class="hl-5">ta</span><span class="hl-2">.</span><span class="hl-1">rsi</span><span class="hl-2">(</span><span class="hl-5">close</span><span class="hl-2">[</span><span class="hl-8">1</span><span class="hl-2">], </span><span class="hl-8">14</span><span class="hl-2">)</span><br/><br/><span class="hl-0">// Price divergence detection</span><br/><span class="hl-5">bullish_div</span><span class="hl-2"> = </span><span class="hl-5">close</span><span class="hl-2"> &lt; </span><span class="hl-5">close</span><span class="hl-2">[</span><span class="hl-8">5</span><span class="hl-2">] </span><span class="hl-5">and</span><span class="hl-2"> </span><span class="hl-5">rsi</span><span class="hl-2"> &gt; </span><span class="hl-5">rsi</span><span class="hl-2">[</span><span class="hl-8">5</span><span class="hl-2">] </span><span class="hl-5">and</span><span class="hl-2"> </span><span class="hl-5">rsi</span><span class="hl-2"> &lt; </span><span class="hl-8">35</span><br/><span class="hl-5">bearish_div</span><span class="hl-2"> = </span><span class="hl-5">close</span><span class="hl-2"> &gt; </span><span class="hl-5">close</span><span class="hl-2">[</span><span class="hl-8">5</span><span class="hl-2">] </span><span class="hl-5">and</span><span class="hl-2"> </span><span class="hl-5">rsi</span><span class="hl-2"> &lt; </span><span class="hl-5">rsi</span><span class="hl-2">[</span><span class="hl-8">5</span><span class="hl-2">] </span><span class="hl-5">and</span><span class="hl-2"> </span><span class="hl-5">rsi</span><span class="hl-2"> &gt; </span><span class="hl-8">65</span><br/><br/><span class="hl-0">// Volume confirmation</span><br/><span class="hl-5">vol_spike</span><span class="hl-2"> = </span><span class="hl-5">volume</span><span class="hl-2"> &gt; </span><span class="hl-5">ta</span><span class="hl-2">.</span><span class="hl-1">sma</span><span class="hl-2">(</span><span class="hl-5">volume</span><span class="hl-2">, </span><span class="hl-8">20</span><span class="hl-2">) * </span><span class="hl-8">1.5</span><br/><br/><span class="hl-0">// Signals</span><br/><span class="hl-5">long_signal</span><span class="hl-2"> = </span><span class="hl-5">bullish_div</span><span class="hl-2"> </span><span class="hl-5">and</span><span class="hl-2"> </span><span class="hl-5">vol_spike</span><br/><span class="hl-5">short_signal</span><span class="hl-2"> = </span><span class="hl-5">bearish_div</span><span class="hl-2"> </span><span class="hl-5">and</span><span class="hl-2"> </span><span class="hl-5">vol_spike</span><br/><br/><span class="hl-1">plot</span><span class="hl-2">(</span><span class="hl-5">long_signal</span><span class="hl-2"> ? </span><span class="hl-8">1</span><span class="hl-2"> : </span><span class="hl-5">short_signal</span><span class="hl-2"> ? -</span><span class="hl-8">1</span><span class="hl-2"> : </span><span class="hl-8">0</span><span class="hl-2">, </span><span class="hl-3">&quot;Signal&quot;</span><span class="hl-2">)</span><br/><span class="hl-1">plot</span><span class="hl-2">(</span><span class="hl-5">close</span><span class="hl-2">, </span><span class="hl-3">&quot;Close&quot;</span><span class="hl-2">)</span><br/><span class="hl-1">plot</span><span class="hl-2">(</span><span class="hl-5">close</span><span class="hl-2"> * </span><span class="hl-8">0.98</span><span class="hl-2">, </span><span class="hl-3">&quot;StopLoss&quot;</span><span class="hl-2">) </span><span class="hl-0">// 2% stop</span><br/><span class="hl-1">plot</span><span class="hl-2">(</span><span class="hl-5">close</span><span class="hl-2"> * </span><span class="hl-8">1.03</span><span class="hl-2">, </span><span class="hl-3">&quot;TakeProfit&quot;</span><span class="hl-2">) </span><span class="hl-0">// 3% target</span><br/><span class="hl-7">EOF</span>
</code><button type="button">Copy</button></pre>
<p><strong>Claude Code</strong>: <em>Running backtest on October 2025 (crypto crash period)...</em></p>
<pre><code class="javascript"><span class="hl-5">npm</span><span class="hl-2"> </span><span class="hl-5">start</span><span class="hl-2"> -- \</span><br/><span class="hl-2"> --</span><span class="hl-5">mode</span><span class="hl-2"> </span><span class="hl-5">backtest</span><span class="hl-2"> \</span><br/><span class="hl-2"> --</span><span class="hl-5">frame</span><span class="hl-2"> </span><span class="hl-5">October2025</span><span class="hl-2"> \</span><br/><span class="hl-2"> --</span><span class="hl-5">strategy</span><span class="hl-2"> </span><span class="hl-5">rsi</span><span class="hl-2">-</span><span class="hl-5">divergence</span><span class="hl-2"> \</span><br/><span class="hl-2"> --</span><span class="hl-5">symbol</span><span class="hl-2"> </span><span class="hl-7">BTCUSDT</span>
</code><button type="button">Copy</button></pre>
<p><em><strong>Backtest Results:</strong></em></p>
<p><img src="../media/poor_pnl.png" alt="Poor PNL"></p>
<p><em><strong>Claude Code</strong></em>: <em>Hmm, losses during volatility. I'll add dynamic stops based on ATR and a trend filter...</em></p>
<pre><code class="bash"><span class="hl-0"># Modifies Pine Script</span><br/><span class="hl-1">str_replace_editor</span><span class="hl-2"> </span><span class="hl-13">\</span><br/><span class="hl-2"> </span><span class="hl-4">--path</span><span class="hl-2"> </span><span class="hl-3">config/source/rsi_divergence.pine</span><span class="hl-2"> </span><span class="hl-13">\</span><br/><span class="hl-2"> </span><span class="hl-4">--old_str</span><span class="hl-2"> </span><span class="hl-3">&quot;plot(close * 0.98, </span><span class="hl-13">\&quot;</span><span class="hl-3">StopLoss</span><span class="hl-13">\&quot;</span><span class="hl-3">)&quot;</span><span class="hl-2"> </span><span class="hl-13">\</span><br/><span class="hl-2"> </span><span class="hl-4">--new_str</span><span class="hl-2"> </span><span class="hl-3">&quot;atr = ta.atr(14)\nplot(close - atr * 2, </span><span class="hl-13">\&quot;</span><span class="hl-3">StopLoss</span><span class="hl-13">\&quot;</span><span class="hl-3">)&quot;</span><br/><br/><span class="hl-0"># Adds 4H trend filter</span><br/><span class="hl-1">cat</span><span class="hl-2"> &gt; </span><span class="hl-3">config/source/trend_filter.pine</span><span class="hl-2"> &lt;&lt; </span><span class="hl-2">&#39;EOF&#39;</span><br/><span class="hl-3">//@version=5</span><br/><span class="hl-3">indicator(&quot;Trend Filter 4H&quot;)</span><br/><br/><span class="hl-3">ema200 = ta.ema(close, 200)</span><br/><span class="hl-3">bullish_trend = close &gt; ema200</span><br/><span class="hl-3">bearish_trend = close &lt; ema200</span><br/><br/><span class="hl-3">plot(bullish_trend ? 1 : bearish_trend ? -1 : 0, &quot;TrendBias&quot;)</span><br/><span class="hl-2">EOF</span>
</code><button type="button">Copy</button></pre>
<p><em><strong>Claude Code</strong></em>: <em>Rerunning backtest with dynamic stops + trend filter...</em></p>
<p><img src="../media/good_pnl.png" alt="Good PNL"></p>
<p><em><strong>Claude Code</strong></em>: <em>Strategy is now profitable. Deploying to paper trading for live validation...</em></p>
<pre><code class="bash"><span class="hl-1">npm</span><span class="hl-2"> </span><span class="hl-3">start</span><span class="hl-2"> </span><span class="hl-4">--</span><span class="hl-2"> </span><span class="hl-13">\</span><br/><span class="hl-2"> </span><span class="hl-4">--mode</span><span class="hl-2"> </span><span class="hl-3">paper</span><span class="hl-2"> </span><span class="hl-13">\</span><br/><span class="hl-2"> </span><span class="hl-4">--strategy</span><span class="hl-2"> </span><span class="hl-3">rsi-divergence</span><span class="hl-2"> </span><span class="hl-13">\</span><br/><span class="hl-2"> </span><span class="hl-4">--symbol</span><span class="hl-2"> </span><span class="hl-3">BTCUSDT</span>
</code><button type="button">Copy</button></pre>
<a id="thank-you-for-your-attention" class="tsd-anchor"></a><h2 class="tsd-anchor-link">Thank you for Your Attention!<a href="#thank-you-for-your-attention" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2><p><img src="../media/knight.png" alt="knight"></p>
</div></div><div class="col-sidebar"><div class="page-menu"><div class="tsd-navigation settings"><details class="tsd-accordion"><summary class="tsd-accordion-summary"><h3><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>Settings</h3></summary><div class="tsd-accordion-details"><div class="tsd-filter-visibility"><span class="settings-label">Member Visibility</span><ul id="tsd-filter-options"><li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-protected" name="protected"/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Protected</span></label></li><li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-inherited" name="inherited" checked/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Inherited</span></label></li><li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-external" name="external"/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>External</span></label></li></ul></div><div class="tsd-theme-toggle"><label class="settings-label" for="tsd-theme">Theme</label><select id="tsd-theme"><option value="os">OS</option><option value="light">Light</option><option value="dark">Dark</option></select></div></div></details></div><details open class="tsd-accordion tsd-page-navigation"><summary class="tsd-accordion-summary"><h3><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>On This Page</h3></summary><div class="tsd-accordion-details"><a href="#๐Ÿ“Š-how-ai-got-hands-for-stock-trading"><span>๐Ÿ“Š <wbr/>How AI <wbr/>Got <wbr/>Hands for <wbr/>Stock <wbr/>Trading</span></a><ul><li><a href="#the-tradingview-problem-the-human-becomes-the-bottleneck"><span>The <wbr/>Trading<wbr/>View <wbr/>Problem: <wbr/>The <wbr/>Human <wbr/>Becomes the <wbr/>Bottleneck</span></a></li><li><a href="#the-architectural-flaw-of-pine-script"><span>The <wbr/>Architectural <wbr/>Flaw of <wbr/>Pine <wbr/>Script</span></a></li><li><a href="#and-now-it-has-hands"><span>And <wbr/>Now <wbr/>It <wbr/>Has <wbr/>Hands</span></a></li><li><a href="#fixing-pine-script"><span>Fixing <wbr/>Pine <wbr/>Script</span></a></li><li><a href="#how-claude-code-uses-this"><span>How <wbr/>Claude <wbr/>Code <wbr/>Uses <wbr/>This</span></a></li><li><a href="#example-of-creating-a-trading-bot"><span>Example of <wbr/>Creating a <wbr/>Trading <wbr/>Bot</span></a></li><li><a href="#thank-you-for-your-attention"><span>Thank you for <wbr/>Your <wbr/>Attention!</span></a></li></ul></div></details></div><div class="site-menu"><nav class="tsd-navigation"><a href="../modules.html">backtest-kit</a><ul class="tsd-small-nested-navigation" id="tsd-nav-container"><li>Loading...</li></ul></nav></div></div></div><footer><p class="tsd-generator">Generated using <a href="https://typedoc.org/" target="_blank">TypeDoc</a></p></footer><div class="overlay"></div></body></html>
<!-- Yandex.Metrika counter -->
<script type="text/javascript">
(function(m,e,t,r,i,k,a){
m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)};
m[i].l=1*new Date();
for (var j = 0; j < document.scripts.length; j++) {if (document.scripts[j].src === r) { return; }}
k=e.createElement(t),a=e.getElementsByTagName(t)[0],k.async=1,k.src=r,a.parentNode.insertBefore(k,a)
})(window, document,'script','https://mc.yandex.ru/metrika/tag.js?id=105455585', 'ym');
ym(105455585, 'init', {ssr:true, webvisor:true, clickmap:true, ecommerce:"dataLayer", accurateTrackBounce:true, trackLinks:true});
</script>
<noscript><div><img src="https://mc.yandex.ru/watch/105455585" style="position:absolute; left:-9999px;" alt="" /></div></noscript>
<!-- /Yandex.Metrika counter -->
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-3MQZEBBDDR"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-3MQZEBBDDR');
</script>