<?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: Fxm Brand</title>
    <description>The latest articles on DEV Community by Fxm Brand (@fxmbrand).</description>
    <link>https://dev.to/fxmbrand</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3671366%2F602202fe-b8ca-4afa-b630-ae26bf06d56f.png</url>
      <title>DEV Community: Fxm Brand</title>
      <link>https://dev.to/fxmbrand</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/fxmbrand"/>
    <language>en</language>
    <item>
      <title>Backtesting Memecoin Trading Strategies Using Historical Data</title>
      <dc:creator>Fxm Brand</dc:creator>
      <pubDate>Mon, 31 Aug 2026 18:00:14 +0000</pubDate>
      <link>https://dev.to/fxmbrand/backtesting-memecoin-trading-strategies-using-historical-data-mja</link>
      <guid>https://dev.to/fxmbrand/backtesting-memecoin-trading-strategies-using-historical-data-mja</guid>
      <description>&lt;p&gt;&lt;strong&gt;&amp;gt; Practical guide for developers on backtesting memecoin strategies. Covers data sources, realistic slippage &amp;amp; liquidity simulation, common pitfalls, performance metrics, and how to avoid overfitting.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Most memecoin strategies look incredible in hindsight. Almost none survive proper backtesting.&lt;/p&gt;

&lt;p&gt;This article is a practical, developer-focused guide to backtesting memecoin strategies with a realistic mindset. We will cover:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why standard crypto backtesting fails on memecoins&lt;/li&gt;
&lt;li&gt;Where to get usable historical data&lt;/li&gt;
&lt;li&gt;How to simulate fills, slippage, and liquidity constraints&lt;/li&gt;
&lt;li&gt;Core performance metrics that actually matter&lt;/li&gt;
&lt;li&gt;Common biases and how to reduce them&lt;/li&gt;
&lt;li&gt;A clean Python architecture you can extend&lt;/li&gt;
&lt;li&gt;When to stop backtesting and move to paper trading&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal is not to produce a perfect equity curve. The goal is to build a process that quickly tells you whether a strategy has any chance of working in live conditions.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Strategy context&lt;/strong&gt;: Backtesting is only useful if the underlying strategy logic is sound. If you want a ready-made memecoin framework with filters and risk rules already designed for this environment, see &lt;a href="https://selar.com/60lw5u0623" rel="noopener noreferrer"&gt;https://selar.com/60lw5u0623&lt;/a&gt;. This article focuses on how to test ideas rigorously.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Why Memecoin Backtesting Is Hard
&lt;/h2&gt;

&lt;p&gt;Traditional crypto backtesting assumptions break down quickly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Survivorship bias&lt;/strong&gt;: Most tokens from six months ago no longer exist or have near-zero liquidity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Look-ahead bias&lt;/strong&gt;: Using data that would not have been available at decision time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unrealistic fills&lt;/strong&gt;: Assuming you can buy $5k of a token that only had $800 of liquidity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Missing social context&lt;/strong&gt;: Many entries were driven by Twitter/Telegram spikes that are hard to reconstruct perfectly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extreme microstructure&lt;/strong&gt;: Spreads, failed transactions, priority fees, and MEV matter far more than on major pairs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Short lifespan&lt;/strong&gt;: Many tokens live for hours or days. Standard daily-bar methods are almost useless.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your backtest does not aggressively punish these realities, the results are fiction.&lt;/p&gt;




&lt;h2&gt;
  
  
  Data Sources for Memecoin Backtests
&lt;/h2&gt;

&lt;p&gt;You need more than OHLCV.&lt;/p&gt;

&lt;p&gt;Useful sources (as of 2026):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Birdeye / DexScreener / GeckoTerminal&lt;/strong&gt; historical endpoints&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Helius&lt;/strong&gt; parsed transaction and enhanced data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flipside / Dune&lt;/strong&gt; for custom SQL on Solana&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bitquery&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Your own archived WebSocket or RPC logs (best long-term solution)&lt;/li&gt;
&lt;li&gt;Community datasets (use with caution)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Minimum viable data per token:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Timestamped price / OHLC (1m or 5m preferred)&lt;/li&gt;
&lt;li&gt;Volume and buy/sell volume split if possible&lt;/li&gt;
&lt;li&gt;Liquidity (SOL or USD) over time&lt;/li&gt;
&lt;li&gt;Number of unique buyers/sellers&lt;/li&gt;
&lt;li&gt;Holder distribution snapshots (harder to get historically)&lt;/li&gt;
&lt;li&gt;Pool creation time and initial liquidity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For social features you will often need to archive X/Telegram data yourself or buy historical firehose access. Many serious teams simply accept that perfect social reconstruction is impossible and focus on on-chain + volume features.&lt;/p&gt;




&lt;h2&gt;
  
  
  Core Principles of Realistic Simulation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Start from pool creation or first meaningful liquidity
&lt;/h3&gt;

&lt;p&gt;Do not backtest a token from an arbitrary date. Begin at the moment it became tradeable with meaningful liquidity.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Model liquidity and price impact
&lt;/h3&gt;

&lt;p&gt;A simple but effective approach:&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;estimate_fill_price&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;side&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size_usd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;liquidity_usd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mid_price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Very rough constant-product style impact
&lt;/span&gt;    &lt;span class="n"&gt;impact&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;size_usd&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;liquidity_usd&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;size_usd&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;side&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;buy&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;mid_price&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&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;impact&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;1.2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# extra buffer
&lt;/span&gt;    &lt;span class="k"&gt;else&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;mid_price&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&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;impact&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;1.2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can later replace this with more accurate curve math or recorded order-book snapshots if available.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Apply fees + priority fees + failed tx probability
&lt;/h3&gt;

&lt;p&gt;Solana trades during congestion often pay meaningful priority fees. Include a base fee + variable priority cost, and occasionally force a failed transaction to simulate reality.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Enforce maximum position size relative to liquidity
&lt;/h3&gt;

&lt;p&gt;Never allow the backtest to take a position larger than X% of available liquidity at that moment.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Use only information available at the decision timestamp
&lt;/h3&gt;

&lt;p&gt;No future candles, no future holder data, no future social volume.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Clean Backtesting Architecture
&lt;/h2&gt;

&lt;p&gt;Suggested structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;data/
  raw/
  processed/
backtester/
  data_loader.py
  simulator.py
  metrics.py
  strategy.py
  runner.py
results/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;High-level flow:&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;# runner.py (conceptual)
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;backtester.data_loader&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_token_data&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;backtester.simulator&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Simulator&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;backtester.strategy&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;MyStrategy&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;backtester.metrics&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;compute_metrics&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run_backtest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token_list&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;strategy_params&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;all_trades&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;token&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;token_list&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_token_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;strategy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MyStrategy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;strategy_params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;sim&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Simulator&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;strategy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;initial_capital&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;10.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# in SOL
&lt;/span&gt;        &lt;span class="n"&gt;trades&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;all_trades&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trades&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;metrics&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;compute_metrics&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;all_trades&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;metrics&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;all_trades&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep the strategy class pure: it should only receive the current row (and limited lookback) and return an action (&lt;code&gt;buy&lt;/code&gt;, &lt;code&gt;sell&lt;/code&gt;, &lt;code&gt;hold&lt;/code&gt;) plus optional size.&lt;/p&gt;




&lt;h2&gt;
  
  
  Implementing the Simulator Core
&lt;/h2&gt;

&lt;p&gt;Key responsibilities of the simulator:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Maintain cash and open positions&lt;/li&gt;
&lt;li&gt;Apply entry/exit logic from the strategy&lt;/li&gt;
&lt;li&gt;Calculate realistic fill prices&lt;/li&gt;
&lt;li&gt;Track fees and slippage&lt;/li&gt;
&lt;li&gt;Record every trade with full context&lt;/li&gt;
&lt;li&gt;Respect max positions and risk limits&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Simplified skeleton:&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;class&lt;/span&gt; &lt;span class="nc"&gt;Simulator&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;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&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;strategy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;initial_capital&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;10.0&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&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="n"&gt;df&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strategy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;strategy&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;capital&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;initial_capital&lt;/span&gt;
        &lt;span class="n"&gt;self&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="bp"&gt;None&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trades&lt;/span&gt; &lt;span class="o"&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;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&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="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&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;self&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;row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&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;iloc&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="n"&gt;signal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strategy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on_bar&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="n"&gt;self&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;iloc&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="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;50&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="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;action&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;buy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;position&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_open_position&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="n"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;action&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sell&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;position&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_close_position&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="n"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Force close any remaining position at the end
&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_close_position&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&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;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="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reason&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;end_of_data&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trades&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside &lt;code&gt;_open_position&lt;/code&gt; and &lt;code&gt;_close_position&lt;/code&gt; you apply the liquidity-aware fill logic, fees, and update capital.&lt;/p&gt;




&lt;h2&gt;
  
  
  Performance Metrics That Matter
&lt;/h2&gt;

&lt;p&gt;Forget “98% win rate” screenshots. Focus on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Net profit in SOL or USD&lt;/strong&gt; after fees and slippage&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Profit factor&lt;/strong&gt; (gross profits / gross losses)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Max drawdown&lt;/strong&gt; (both in % and absolute)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Average R-multiple&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Win rate + average win / average loss&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Number of trades&lt;/strong&gt; (statistical significance)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exposure time&lt;/strong&gt; (% of time in the market)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance by liquidity bucket&lt;/strong&gt; (does it only work on high-liquidity tokens?)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Performance by time-of-day or day-of-week&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sensitivity to slippage assumptions&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Always report results under multiple slippage scenarios (base, +50%, +100%).&lt;/p&gt;




&lt;h2&gt;
  
  
  Reducing Overfitting &amp;amp; Bias
&lt;/h2&gt;

&lt;p&gt;Common traps and mitigations:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Bias / Problem&lt;/th&gt;
&lt;th&gt;Mitigation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Survivorship&lt;/td&gt;
&lt;td&gt;Include dead tokens and failed launches&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Look-ahead&lt;/td&gt;
&lt;td&gt;Strict point-in-time data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Curve fitting&lt;/td&gt;
&lt;td&gt;Walk-forward or out-of-sample periods&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Small sample&lt;/td&gt;
&lt;td&gt;Require minimum number of trades&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Parameter optimization&lt;/td&gt;
&lt;td&gt;Limit free parameters; use sensible ranges&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data snooping&lt;/td&gt;
&lt;td&gt;Keep a true hold-out set of tokens/periods&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Best practice workflow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Design strategy logic on a small set of tokens (in-sample)&lt;/li&gt;
&lt;li&gt;Freeze parameters&lt;/li&gt;
&lt;li&gt;Run on a larger unseen set (out-of-sample)&lt;/li&gt;
&lt;li&gt;If results collapse, the strategy was overfit&lt;/li&gt;
&lt;li&gt;Only then consider paper trading&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Practical Tips for Memecoin-Specific Tests
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Test on multiple market regimes (high activity vs quiet periods)&lt;/li&gt;
&lt;li&gt;Separate results for tokens that lived &amp;lt; 24h vs longer-lived ones&lt;/li&gt;
&lt;li&gt;Measure how early your entry would have been relative to the actual high&lt;/li&gt;
&lt;li&gt;Track the percentage of trades that would have been stopped out by a hard max loss&lt;/li&gt;
&lt;li&gt;Simulate realistic entry delays (1–3 blocks or 5–15 seconds) instead of perfect fills at the signal bar open&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Moving From Backtest to Paper Trading
&lt;/h2&gt;

&lt;p&gt;Even a good backtest is only a filter. The next mandatory step is paper trading on live data with the exact same code path you will use in production.&lt;/p&gt;

&lt;p&gt;Paper trading reveals:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Data feed differences&lt;/li&gt;
&lt;li&gt;Execution latency&lt;/li&gt;
&lt;li&gt;Unexpected API failures&lt;/li&gt;
&lt;li&gt;How your filters behave on brand-new tokens&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Run paper trading for at least several weeks and across different market conditions before risking real capital.&lt;/p&gt;




&lt;h2&gt;
  
  
  Example Minimal Metrics Output
&lt;/h2&gt;

&lt;p&gt;A useful summary might look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Trades: 187
Win rate: 41.2%
Profit factor: 1.68
Net profit: +24.3 SOL
Max drawdown: -8.1 SOL (-18.4%)
Average R: 1.31
Avg trade duration: 27 minutes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Always accompany this with equity curve, drawdown chart, and breakdown by token age / liquidity.&lt;/p&gt;




&lt;h2&gt;
  
  
  When Backtesting Is Not Enough
&lt;/h2&gt;

&lt;p&gt;Some edges are extremely hard to backtest cleanly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pure social-speed advantages&lt;/li&gt;
&lt;li&gt;Very short-term sniping based on pending transactions&lt;/li&gt;
&lt;li&gt;Strategies that rely on private alpha groups or off-chain information&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In those cases, controlled live testing with tiny size becomes the main validation method. Still keep the same logging and risk framework so you can evaluate results later.&lt;/p&gt;




&lt;h2&gt;
  
  
  Putting It All Together
&lt;/h2&gt;

&lt;p&gt;A professional workflow looks like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Collect and clean historical data (including failed tokens)&lt;/li&gt;
&lt;li&gt;Define strategy rules in a pure function/class&lt;/li&gt;
&lt;li&gt;Build a simulator that punishes unrealistic fills&lt;/li&gt;
&lt;li&gt;Run in-sample → freeze → out-of-sample&lt;/li&gt;
&lt;li&gt;Analyze metrics under multiple cost assumptions&lt;/li&gt;
&lt;li&gt;Paper trade the exact same logic&lt;/li&gt;
&lt;li&gt;Only then allocate real capital with strict risk limits&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Most ideas will fail at step 4 or 5. That is the point of the process.&lt;/p&gt;

&lt;p&gt;If you want to skip some of the early strategy design iterations and start from a framework already built around realistic memecoin constraints, the resource at &lt;a href="https://selar.com/60lw5u0623" rel="noopener noreferrer"&gt;https://selar.com/60lw5u0623&lt;/a&gt; provides a strong foundation you can then backtest and adapt.&lt;/p&gt;




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

&lt;p&gt;Backtesting memecoin strategies is less about finding a holy grail and more about rapidly discarding bad ideas. The developers who survive are usually the ones who are willing to kill their own strategies when the data says they do not work.&lt;/p&gt;

&lt;p&gt;Build the infrastructure once. Make the simulator harsh. Demand out-of-sample proof. Then move to paper trading with the same code you will run live.&lt;/p&gt;

&lt;p&gt;That process, repeated consistently, is far more valuable than any single backtest result.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Related articles in this series&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Production-ready CCXT trading bot&lt;/li&gt;
&lt;li&gt;Solana memecoin sniper architecture&lt;/li&gt;
&lt;li&gt;Memecoin strategies: from hype to on-chain signals&lt;/li&gt;
&lt;li&gt;TradingView webhooks to custom bots&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Resource&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://selar.com/60lw5u0623" rel="noopener noreferrer"&gt;Memecoin Trading Strategy&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;



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

&lt;/div&gt;

</description>
      <category>python</category>
      <category>memecoin</category>
      <category>backtesting</category>
      <category>solana</category>
    </item>
    <item>
      <title>Automating Crypto Trades with TradingView Webhooks and Custom Bots</title>
      <dc:creator>Fxm Brand</dc:creator>
      <pubDate>Mon, 31 Aug 2026 17:15:29 +0000</pubDate>
      <link>https://dev.to/fxmbrand/automating-crypto-trades-with-tradingview-webhooks-and-custom-bots-5b97</link>
      <guid>https://dev.to/fxmbrand/automating-crypto-trades-with-tradingview-webhooks-and-custom-bots-5b97</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;_&lt;br&gt;
Complete developer guide to connecting TradingView alerts to your own trading bot via webhooks. Covers Pine Script alerts, secure webhook receivers, order execution, risk checks, and production deployment.&lt;br&gt;
tags: python, tradingview, crypto, tradingbots, webhooks, fastapi_&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;TradingView is still one of the best charting and signal platforms available. Many developers already have working Pine Script strategies or indicators they trust. The missing piece is reliable, automated execution.&lt;/p&gt;

&lt;p&gt;This guide shows you how to turn TradingView alerts into real orders using webhooks and a custom bot. We’ll cover:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Writing alerts that send structured data&lt;/li&gt;
&lt;li&gt;Building a secure webhook receiver (FastAPI)&lt;/li&gt;
&lt;li&gt;Validating and parsing incoming signals&lt;/li&gt;
&lt;li&gt;Connecting the signals to exchange execution (CCXT for CEX, or your Solana executor)&lt;/li&gt;
&lt;li&gt;Adding risk checks before any order is sent&lt;/li&gt;
&lt;li&gt;Handling failures, retries, and logging&lt;/li&gt;
&lt;li&gt;Deploying the system so it stays online&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The result is a clean separation: TradingView handles detection and charting, your bot handles risk and execution.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note for memecoin traders&lt;/strong&gt;: TradingView works well for larger-cap pairs and some Solana tokens that have reliable data feeds. For pure low-cap memecoin sniping and on-chain filters, you will still want dedicated listeners. If you need a complete memecoin-focused strategy framework, see &lt;a href="https://selar.com/60lw5u0623" rel="noopener noreferrer"&gt;https://selar.com/60lw5u0623&lt;/a&gt;. This article focuses on the TradingView → bot bridge that many hybrid systems use.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Why TradingView + Webhooks?
&lt;/h2&gt;

&lt;p&gt;Advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Excellent charting and backtesting tools&lt;/li&gt;
&lt;li&gt;Large library of community scripts&lt;/li&gt;
&lt;li&gt;Alerts can fire on any condition you can code in Pine&lt;/li&gt;
&lt;li&gt;Webhooks let you push signals to any server you control&lt;/li&gt;
&lt;li&gt;No need to poll TradingView APIs constantly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Limitations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Alert frequency is limited by your TradingView plan&lt;/li&gt;
&lt;li&gt;Data quality on very new or low-liquidity memecoins can be poor&lt;/li&gt;
&lt;li&gt;You are still responsible for execution quality, slippage, and risk&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This architecture is ideal when you already have a Pine Script edge and want reliable automation without rewriting everything in Python.&lt;/p&gt;




&lt;h2&gt;
  
  
  High-Level Architecture
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TradingView Alert (Pine Script)
        ↓  HTTPS POST
Your Webhook Receiver (FastAPI / Flask)
        ↓  Validate + Parse
Risk Manager
        ↓  Approved
Execution Layer (CCXT or Solana bot)
        ↓
Exchange / DEX
        ↓
Logging + Telegram Alerts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keeping these layers separate makes the system easier to debug and improve.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1: Creating Useful Alerts in Pine Script
&lt;/h2&gt;

&lt;p&gt;A good alert message is structured and machine-readable. Avoid free-text messages when possible.&lt;/p&gt;

&lt;p&gt;Example alert message format (JSON-like):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{"strategy":"momentum_v2","symbol":"BTCUSDT","side":"buy","price":67250.5,"timeframe":"15","key":"YOUR_SECRET"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In Pine Script you can build this with &lt;code&gt;alert()&lt;/code&gt; or the newer &lt;code&gt;alert_message&lt;/code&gt; parameter in strategy orders.&lt;/p&gt;

&lt;p&gt;Simple example inside a strategy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//@version=5
strategy("Webhook Example", overlay=true)

longCondition = ta.crossover(ta.sma(close, 14), ta.sma(close, 28))
if (longCondition)
    strategy.entry("Long", strategy.long)
    alert('{"strategy":"sma_cross","symbol":"' + syminfo.ticker + '","side":"buy","key":"YOUR_SECRET"}', alert.freq_once_per_bar)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For indicators (not strategies) you can use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if longCondition
    alert('{"side":"buy","symbol":"' + syminfo.ticker + '","key":"YOUR_SECRET"}')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;Always include a secret key so your server can reject unauthorized requests&lt;/li&gt;
&lt;li&gt;Include symbol, side, and any extra context (timeframe, stop level, strategy name)&lt;/li&gt;
&lt;li&gt;Test alerts first with a service like webhook.site before pointing them at your real server&lt;/li&gt;
&lt;li&gt;Remember TradingView has rate limits on how many alerts can fire&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 2: Building the Webhook Receiver
&lt;/h2&gt;

&lt;p&gt;We will use FastAPI because it is fast, modern, and easy to secure.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;fastapi uvicorn python-dotenv httpx loguru tenacity ccxt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Basic receiver:&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;# main.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HTTPException&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Header&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BaseModel&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dotenv&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_dotenv&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;loguru&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;hmac&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;

&lt;span class="nf"&gt;load_dotenv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;TradingView Webhook Bot&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;WEBHOOK_SECRET&lt;/span&gt; &lt;span class="o"&gt;=&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;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;WEBHOOK_SECRET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ALLOWED_IPS&lt;/span&gt; &lt;span class="o"&gt;=&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;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ALLOWED_IPS&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="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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# optional
&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AlertPayload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;strategy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
    &lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;side&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
    &lt;span class="n"&gt;timeframe&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
    &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;

&lt;span class="nd"&gt;@app.post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/webhook&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;tradingview_webhook&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AlertPayload&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# 1. Basic secret check
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;payload&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="n"&gt;WEBHOOK_SECRET&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Invalid secret key received&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;HTTPException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;403&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;detail&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Forbidden&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 2. Optional IP allowlist (TradingView publishes their IPs)
&lt;/span&gt;    &lt;span class="n"&gt;client_ip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ALLOWED_IPS&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;client_ip&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ALLOWED_IPS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warning&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;Request from non-allowed IP: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;client_ip&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;raise&lt;/span&gt; &lt;span class="nc"&gt;HTTPException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;403&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;detail&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Forbidden&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&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;Received alert: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;()&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="c1"&gt;# 3. Pass to risk + execution
&lt;/span&gt;    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;process_alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&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;status&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;ok&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;result&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uvicorn main:app &lt;span class="nt"&gt;--host&lt;/span&gt; 0.0.0.0 &lt;span class="nt"&gt;--port&lt;/span&gt; 8000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For production you will put this behind Nginx or Caddy with HTTPS (Let’s Encrypt).&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3: Processing the Alert &amp;amp; Risk Checks
&lt;/h2&gt;

&lt;p&gt;Never execute blindly. Always run risk checks first.&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;# risk.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;loguru&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;

&lt;span class="n"&gt;MAX_POSITIONS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="n"&gt;MAX_RISK_PER_TRADE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.02&lt;/span&gt;  &lt;span class="c1"&gt;# 2% of equity
&lt;/span&gt;&lt;span class="n"&gt;DAILY_LOSS_LIMIT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.05&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RiskManager&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;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;open_positions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;daily_pnl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;approve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;side&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;open_positions&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;MAX_POSITIONS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Max open positions reached&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="bp"&gt;False&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;daily_pnl&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;DAILY_LOSS_LIMIT&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Daily loss limit hit — trading paused&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="bp"&gt;False&lt;/span&gt;
        &lt;span class="c1"&gt;# Add more checks: symbol whitelist, max size, etc.
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the main flow:&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;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process_alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AlertPayload&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Normalize symbol (TradingView uses BTCUSDT, CCXT often wants BTC/USDT)
&lt;/span&gt;    &lt;span class="n"&gt;symbol&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;normalize_symbol&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Calculate size (example)
&lt;/span&gt;    &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;calculate_position_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;risk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;RiskManager&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;risk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;approve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;side&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&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;executed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;reason&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;risk_rejected&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;# Execute
&lt;/span&gt;    &lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;execute_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;side&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&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;executed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;order&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 4: Execution Layer
&lt;/h2&gt;

&lt;p&gt;You can reuse the CCXT infrastructure from the earlier production bot article, or call your Solana executor.&lt;/p&gt;

&lt;p&gt;Simple CCXT example:&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;ccxt.async_support&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;ccxt&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;loguru&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;

&lt;span class="n"&gt;exchange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ccxt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;binance&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;apiKey&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&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;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;BINANCE_KEY&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;secret&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&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;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;BINANCE_SECRET&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;enableRateLimit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;options&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;defaultType&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;future&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;  &lt;span class="c1"&gt;# or "spot"
&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;execute_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;side&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&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;order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;market&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;side&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;side&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;success&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;Order placed: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;id&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="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;order&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="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&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;Execution failed: &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;raise&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For Solana memecoins you would call your Jupiter or Raydium execution function instead.&lt;/p&gt;

&lt;p&gt;Always:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Log the full order response&lt;/li&gt;
&lt;li&gt;Send a Telegram notification on fill or failure&lt;/li&gt;
&lt;li&gt;Update your internal position tracker&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 5: Security Best Practices
&lt;/h2&gt;

&lt;p&gt;Webhook endpoints are public. Protect them properly:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Shared secret&lt;/strong&gt; in the alert message (as shown)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IP allowlisting&lt;/strong&gt; (TradingView publishes their outbound IPs)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HTTPS only&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rate limiting&lt;/strong&gt; on your endpoint&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Request signature&lt;/strong&gt; (more advanced — HMAC of the body)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Idempotency keys&lt;/strong&gt; so the same alert cannot trigger twice&lt;/li&gt;
&lt;li&gt;Run the receiver with minimal privileges&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Example simple rate limiter with FastAPI middleware or a library like &lt;code&gt;slowapi&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 6: Making It Production-Ready
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Logging &amp;amp; Alerts
&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;from&lt;/span&gt; &lt;span class="n"&gt;loguru&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;

&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;logs/webhook_{time}.log&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rotation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;20 MB&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;retention&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;10 days&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;INFO&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;Send critical events to Telegram:&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;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;notify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# simple httpx POST to Telegram Bot API
&lt;/span&gt;    &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Error Handling &amp;amp; Retries
&lt;/h3&gt;

&lt;p&gt;Use &lt;code&gt;tenacity&lt;/code&gt; for transient exchange errors. Catch specific exceptions (rate limits, insufficient funds, network issues) and decide whether to retry or alert.&lt;/p&gt;

&lt;h3&gt;
  
  
  Position Tracking
&lt;/h3&gt;

&lt;p&gt;Keep a lightweight local state (Redis or even a SQLite file) of open positions so the bot knows what it currently holds. This prevents duplicate entries and helps with exit logic.&lt;/p&gt;

&lt;h3&gt;
  
  
  Health Checks
&lt;/h3&gt;

&lt;p&gt;Add a simple &lt;code&gt;/health&lt;/code&gt; endpoint that returns 200 if the bot is alive and can reach the exchange.&lt;/p&gt;




&lt;h2&gt;
  
  
  Deployment
&lt;/h2&gt;

&lt;p&gt;Recommended path:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;VPS (Hetzner, DigitalOcean, etc.)&lt;/li&gt;
&lt;li&gt;Docker + Docker Compose&lt;/li&gt;
&lt;li&gt;Caddy or Nginx for automatic HTTPS&lt;/li&gt;
&lt;li&gt;Systemd or Docker restart policies&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Example &lt;code&gt;docker-compose.yml&lt;/code&gt; snippet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;webhook-bot&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8000:8000"&lt;/span&gt;
    &lt;span class="na"&gt;env_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.env&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./logs:/app/logs&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Point your TradingView alert URL to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://yourdomain.com/webhook
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Test thoroughly with the TradingView “Test” button and with small size live orders.&lt;/p&gt;




&lt;h2&gt;
  
  
  Advanced Patterns
&lt;/h2&gt;

&lt;p&gt;Once the basic bridge works you can add:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multiple strategy support (different Pine scripts → different risk parameters)&lt;/li&gt;
&lt;li&gt;Exit alerts (separate webhook messages for closing positions)&lt;/li&gt;
&lt;li&gt;Dynamic position sizing based on TradingView-sent stop distance&lt;/li&gt;
&lt;li&gt;Paper-trading mode that logs signals without sending orders&lt;/li&gt;
&lt;li&gt;Multi-exchange routing (send BTC signals to Binance, SOL signals to your Solana bot)&lt;/li&gt;
&lt;li&gt;Confirmation candles or secondary filters before execution&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Common Pitfalls
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Alert message format breaks when you change Pine Script&lt;/li&gt;
&lt;li&gt;Forgetting to normalize symbols (BTCUSDT vs BTC/USDT)&lt;/li&gt;
&lt;li&gt;No secret → anyone can spam your endpoint&lt;/li&gt;
&lt;li&gt;No risk checks → one bad alert can open oversized positions&lt;/li&gt;
&lt;li&gt;TradingView plan limits causing missed alerts&lt;/li&gt;
&lt;li&gt;Not handling partial fills or order rejections cleanly&lt;/li&gt;
&lt;li&gt;Running without HTTPS or proper authentication&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  When to Use This vs Pure On-Chain Bots
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;TradingView webhooks shine when&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You trade liquid pairs with good TradingView data&lt;/li&gt;
&lt;li&gt;Your edge is chart/pattern/indicator based&lt;/li&gt;
&lt;li&gt;You already have working Pine Scripts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Pure on-chain / sniper bots are better when&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You focus on brand-new memecoins&lt;/li&gt;
&lt;li&gt;You need block-level speed and custom filters&lt;/li&gt;
&lt;li&gt;Social + on-chain data is more important than classical technical analysis&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Many serious traders run both systems side by side.&lt;/p&gt;

&lt;p&gt;For a dedicated memecoin strategy that already includes filters, scoring, and risk rules, the framework at &lt;a href="https://selar.com/60lw5u0623" rel="noopener noreferrer"&gt;https://selar.com/60lw5u0623&lt;/a&gt; remains the recommended starting point.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Checklist
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Pine Script sends clean, structured JSON messages&lt;/li&gt;
&lt;li&gt;[ ] Webhook secret is validated on every request&lt;/li&gt;
&lt;li&gt;[ ] HTTPS is enabled&lt;/li&gt;
&lt;li&gt;[ ] Risk manager runs before any order&lt;/li&gt;
&lt;li&gt;[ ] Execution errors are caught and alerted&lt;/li&gt;
&lt;li&gt;[ ] Positions are tracked&lt;/li&gt;
&lt;li&gt;[ ] Logs and Telegram notifications are working&lt;/li&gt;
&lt;li&gt;[ ] System restarts automatically on failure&lt;/li&gt;
&lt;li&gt;[ ] You have tested with small size&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You now have a complete, production-oriented bridge between TradingView and your own execution engine. This pattern scales cleanly and keeps your strategy logic where it is easiest to maintain (Pine) while giving you full control over risk and order management.&lt;/p&gt;

&lt;p&gt;Build carefully, size conservatively, and keep improving the risk layer — that is usually where the real durability comes from.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Related articles in this series&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Production-ready CCXT bot&lt;/li&gt;
&lt;li&gt;Solana memecoin sniper architecture&lt;/li&gt;
&lt;li&gt;Memecoin strategies: hype + on-chain signals&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Resource&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://selar.com/60lw5u0623" rel="noopener noreferrer"&gt;Memecoin Trading Strategy&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;



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

&lt;/div&gt;

</description>
      <category>webhooks</category>
      <category>tradingview</category>
      <category>webdev</category>
      <category>ai</category>
    </item>
    <item>
      <title>Do you trade memecoin? You'll love this article..</title>
      <dc:creator>Fxm Brand</dc:creator>
      <pubDate>Mon, 31 Aug 2026 16:54:49 +0000</pubDate>
      <link>https://dev.to/fxmbrand/do-you-trade-memecoin-youll-love-this-article-4fc8</link>
      <guid>https://dev.to/fxmbrand/do-you-trade-memecoin-youll-love-this-article-4fc8</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/fxmbrand/memecoin-trading-strategies-for-developers-from-hype-to-on-chain-signals-24i4" class="crayons-story__hidden-navigation-link"&gt;Memecoin Trading Strategies for Developers — From Hype to On-Chain Signals&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/fxmbrand" class="crayons-avatar  crayons-avatar--l  "&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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3671366%2F602202fe-b8ca-4afa-b630-ae26bf06d56f.png" alt="fxmbrand profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/fxmbrand" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Fxm Brand
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Fxm Brand
                
                
              
              &lt;div id="story-author-preview-content-4539038" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/fxmbrand" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3671366%2F602202fe-b8ca-4afa-b630-ae26bf06d56f.png" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Fxm Brand&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/fxmbrand/memecoin-trading-strategies-for-developers-from-hype-to-on-chain-signals-24i4" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Aug 31&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/fxmbrand/memecoin-trading-strategies-for-developers-from-hype-to-on-chain-signals-24i4" id="article-link-4539038"&gt;
          Memecoin Trading Strategies for Developers — From Hype to On-Chain Signals
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/webdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;webdev&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/cryptocurrency"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;cryptocurrency&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/memecoin"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;memecoin&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/python"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;python&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/fxmbrand/memecoin-trading-strategies-for-developers-from-hype-to-on-chain-signals-24i4" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;1&lt;span class="hidden s:inline"&gt;&amp;nbsp;reaction&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/fxmbrand/memecoin-trading-strategies-for-developers-from-hype-to-on-chain-signals-24i4#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            7 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
    </item>
    <item>
      <title>Memecoin Trading Strategies for Developers — From Hype to On-Chain Signals</title>
      <dc:creator>Fxm Brand</dc:creator>
      <pubDate>Mon, 31 Aug 2026 16:53:46 +0000</pubDate>
      <link>https://dev.to/fxmbrand/memecoin-trading-strategies-for-developers-from-hype-to-on-chain-signals-24i4</link>
      <guid>https://dev.to/fxmbrand/memecoin-trading-strategies-for-developers-from-hype-to-on-chain-signals-24i4</guid>
      <description>&lt;p&gt;Most memecoin “strategies” you see online are just screenshots of lucky entries. Real edge comes from systematically combining &lt;strong&gt;social hype&lt;/strong&gt; with &lt;strong&gt;on-chain reality&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This article is written for developers who want to move beyond blind sniping or pure FOMO. We’ll break down practical signal sources, how to score them, how to filter noise, and how to turn those signals into structured entry and exit rules. The focus is on Solana (where most of the current action lives), but the principles apply across chains.&lt;/p&gt;

&lt;p&gt;By the end you should have a clear mental model — and a starting architecture — for a strategy that can actually be coded, tested, and improved.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important&lt;/strong&gt;: Strategy design is only half the battle. Execution speed, position sizing, and hard risk limits matter just as much. If you want a complete, ready-to-adapt memecoin trading framework with tested filters and risk rules, the resource at &lt;a href="https://selar.com/60lw5u0623" rel="noopener noreferrer"&gt;https://selar.com/60lw5u0623&lt;/a&gt; is built specifically for this environment.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The Reality of Memecoin Markets
&lt;/h2&gt;

&lt;p&gt;Memecoins are not traditional assets. They are narrative-driven, liquidity-sensitive, and dominated by short time horizons. Key characteristics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Extreme volatility and rapid regime changes&lt;/li&gt;
&lt;li&gt;Very low average liquidity outside the top names&lt;/li&gt;
&lt;li&gt;High rate of rugs, honeypots, and soft rugs&lt;/li&gt;
&lt;li&gt;Social platforms (X, Telegram, Discord) act as primary discovery channels&lt;/li&gt;
&lt;li&gt;On-chain data is the only source of truth that cannot be easily faked&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A profitable approach almost always combines:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Early detection of attention (hype)&lt;/li&gt;
&lt;li&gt;Verification that the token is not an obvious scam (on-chain filters)&lt;/li&gt;
&lt;li&gt;Disciplined entries and even more disciplined exits&lt;/li&gt;
&lt;li&gt;Strict capital and risk controls&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Pure speed without filters loses money. Pure fundamental analysis is too slow. The middle path is where most sustainable edges live.&lt;/p&gt;




&lt;h2&gt;
  
  
  Layer 1: Social / Hype Signals
&lt;/h2&gt;

&lt;p&gt;Social signals are noisy but extremely valuable for timing. The goal is not to read every tweet — it is to detect sudden spikes in attention.&lt;/p&gt;

&lt;h3&gt;
  
  
  Primary Sources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;X (Twitter)&lt;/strong&gt;: Keyword volume, influencer mentions, new account clusters, engagement velocity&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Telegram&lt;/strong&gt;: New group creation, member growth rate, message frequency in known alpha channels&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DexScreener / Birdeye trending&lt;/strong&gt;: Already-filtered lists that many bots watch&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Discord &amp;amp; smaller communities&lt;/strong&gt;: Higher signal-to-noise in some cases, harder to scrape&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Practical Scoring Ideas
&lt;/h3&gt;

&lt;p&gt;Instead of binary “buy when mentioned,” build a simple attention score:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Attention Score =
  (unique mentions in last 15 min × weight)
  + (engagement rate × weight)
  + (influencer tier multiplier)
  + (new wallet mentions of the contract)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Developers usually implement this with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;X API (or scraping + proxy rotation if budget is tight)&lt;/li&gt;
&lt;li&gt;Telegram client libraries (Telethon / Pyrogram)&lt;/li&gt;
&lt;li&gt;Webhooks from services that already aggregate social data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Important caveats:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bots and paid shill networks are everywhere&lt;/li&gt;
&lt;li&gt;Many “organic” looking spikes are coordinated&lt;/li&gt;
&lt;li&gt;Social signal alone is almost never enough&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Treat social data as a &lt;strong&gt;timing layer&lt;/strong&gt;, not a safety layer.&lt;/p&gt;




&lt;h2&gt;
  
  
  Layer 2: On-Chain Signals (The Filter)
&lt;/h2&gt;

&lt;p&gt;This is where most of the real work happens. On-chain data lets you reject the majority of garbage before you risk capital.&lt;/p&gt;

&lt;h3&gt;
  
  
  Core Safety Filters (Must-Have)
&lt;/h3&gt;

&lt;p&gt;These should be non-negotiable in any serious system:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Mint authority revoked&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Freeze authority revoked&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Minimum liquidity&lt;/strong&gt; (e.g. &amp;gt; 10–20 SOL or equivalent USD)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LP tokens locked or burned&lt;/strong&gt; (or at least not sitting in the deployer wallet)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Top 10 holders concentration&lt;/strong&gt; below a threshold (e.g. &amp;lt; 35–40%)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No excessive buy/sell tax&lt;/strong&gt; (simulate a small swap)&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Secondary Quality Signals
&lt;/h3&gt;

&lt;p&gt;Once the token passes basic safety:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Liquidity growth rate in the first minutes&lt;/li&gt;
&lt;li&gt;Unique buyer count vs. total volume&lt;/li&gt;
&lt;li&gt;Bundle / sniper concentration in the first blocks&lt;/li&gt;
&lt;li&gt;Deployer wallet history (previous rugs?)&lt;/li&gt;
&lt;li&gt;Token age and whether metadata is properly set&lt;/li&gt;
&lt;li&gt;Presence of a working website + socials (weak signal, but useful)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Volume &amp;amp; Momentum Signals
&lt;/h3&gt;

&lt;p&gt;After entry, or for secondary confirmation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sustained buy volume vs. sell volume&lt;/li&gt;
&lt;li&gt;Number of unique wallets buying in a short window&lt;/li&gt;
&lt;li&gt;Price impact of recent sells (thin books are dangerous)&lt;/li&gt;
&lt;li&gt;Funding or open interest if the token has perps (rare for pure memes)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Many of these checks can be performed via:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Helius / QuickNode enhanced APIs&lt;/li&gt;
&lt;li&gt;Birdeye, DexScreener, or GeckoTerminal APIs&lt;/li&gt;
&lt;li&gt;Direct RPC calls + account data parsing&lt;/li&gt;
&lt;li&gt;Jupiter or Raydium simulation endpoints&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Combining Hype + On-Chain into a Strategy
&lt;/h2&gt;

&lt;p&gt;A clean mental model looks 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;1. Social attention spike detected
2. Extract contract address
3. Run hard safety filters (mint/freeze/LP/holders/tax)
4. If passed → calculate position size
5. Execute entry (preferably via aggregator)
6. Monitor for exit conditions
7. Enforce hard risk limits at every step
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Example Entry Logic (Conceptual)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;IF attention_score &amp;gt; threshold
AND mint_authority_revoked
AND freeze_authority_revoked
AND liquidity_usd &amp;gt; 15000
AND top10_holder_pct &amp;lt; 38
AND simulated_tax &amp;lt; 10%
AND unique_buyers_last_3min &amp;gt; 25
THEN
  size = calculate_position(...)
  execute_buy()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Exit Frameworks That Actually Matter
&lt;/h3&gt;

&lt;p&gt;Entries get all the attention. Exits determine whether you keep any profits.&lt;/p&gt;

&lt;p&gt;Common practical approaches:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Time-based&lt;/strong&gt;: Hard exit after 15–45 minutes if no momentum continuation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Percentage targets&lt;/strong&gt;: Scale out at +40%, +80%, +150%&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trailing stop&lt;/strong&gt; based on recent high or ATR-like measure&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Volume exhaustion&lt;/strong&gt;: Exit when buy volume dries up while price stalls&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Structure break&lt;/strong&gt;: Exit on clear lower high + increasing sell pressure&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hard stop&lt;/strong&gt;: Always have a maximum loss per trade (e.g. –25% to –35%)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Many successful memecoin systems use a combination: partial takes at fixed targets + a trailing mechanism on the remainder + a time stop.&lt;/p&gt;




&lt;h2&gt;
  
  
  Position Sizing for High-Volatility Environments
&lt;/h2&gt;

&lt;p&gt;Standard crypto position sizing often fails here because of gaps and thin liquidity.&lt;/p&gt;

&lt;p&gt;Practical rules used by many developers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Risk a fixed small percentage of the &lt;strong&gt;hot wallet&lt;/strong&gt; per trade (0.5–2%)&lt;/li&gt;
&lt;li&gt;Cap maximum SOL (or USD) per trade regardless of account size&lt;/li&gt;
&lt;li&gt;Limit concurrent open positions (usually 2–5)&lt;/li&gt;
&lt;li&gt;Daily loss limit that pauses the bot&lt;/li&gt;
&lt;li&gt;Never average down on memecoins&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example simple sizing function:&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;position_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hot_wallet_sol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;risk_pct&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_sol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;risk_amount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hot_wallet_sol&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;risk_pct&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="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;risk_amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_sol&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In reality you will also adjust size based on liquidity depth and current volatility.&lt;/p&gt;




&lt;h2&gt;
  
  
  Architecture for a Strategy Engine
&lt;/h2&gt;

&lt;p&gt;A maintainable system usually separates concerns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Signal Ingestion
    ↓
Social Score + On-Chain Metrics
    ↓
Filter Engine (hard rules)
    ↓
Strategy Decision (entry / ignore)
    ↓
Risk Manager (size + limits)
    ↓
Execution Layer (Jupiter / Raydium)
    ↓
Position Monitor + Exit Logic
    ↓
Logging + Alerts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This separation makes it easy to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Swap social data providers&lt;/li&gt;
&lt;li&gt;Add or remove filters without touching execution&lt;/li&gt;
&lt;li&gt;Run the same strategy in paper-trading mode&lt;/li&gt;
&lt;li&gt;A/B test different exit rules&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Common Strategy Archetypes
&lt;/h2&gt;

&lt;p&gt;Here are four patterns that appear repeatedly among developers who last more than one cycle:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Pure Sniper + Heavy Filters
&lt;/h3&gt;

&lt;p&gt;Detect new liquidity → aggressive filters → small size → fast exit. High frequency, low average win rate, needs excellent execution.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Attention Continuation
&lt;/h3&gt;

&lt;p&gt;Wait for social spike + initial pump → enter on first healthy pullback if on-chain metrics remain strong. Slightly slower, often better risk/reward.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Momentum Rider
&lt;/h3&gt;

&lt;p&gt;Enter only after clear volume expansion and multiple unique buyers. Hold for larger moves. Fewer trades, higher variance.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Narrative Basket
&lt;/h3&gt;

&lt;p&gt;Track emerging narratives (AI agents, specific animal themes, political memes, etc.) and allocate small size across several related tokens instead of hunting single home runs.&lt;/p&gt;

&lt;p&gt;Most profitable systems are hybrids of the above.&lt;/p&gt;




&lt;h2&gt;
  
  
  Backtesting &amp;amp; Reality Checks
&lt;/h2&gt;

&lt;p&gt;Memecoin backtesting is hard because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Historical social data is incomplete&lt;/li&gt;
&lt;li&gt;Liquidity and spread conditions change rapidly&lt;/li&gt;
&lt;li&gt;Many tokens disappear&lt;/li&gt;
&lt;li&gt;Slippage during launches is extreme&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Still useful practices:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Replay known successful and failed launches with your filter set&lt;/li&gt;
&lt;li&gt;Measure how many tokens your filters would have rejected&lt;/li&gt;
&lt;li&gt;Track hypothetical win rate, average R-multiple, and max drawdown&lt;/li&gt;
&lt;li&gt;Always assume worse fills than the candle data suggests&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Paper trading on live data for at least 2–4 weeks is almost mandatory before increasing size.&lt;/p&gt;




&lt;h2&gt;
  
  
  Risk Management Is the Real Strategy
&lt;/h2&gt;

&lt;p&gt;You can have mediocre signals and still survive with excellent risk control. The reverse is rarely true.&lt;/p&gt;

&lt;p&gt;Non-negotiable elements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hard maximum loss per trade&lt;/li&gt;
&lt;li&gt;Daily and weekly loss limits that disable new entries&lt;/li&gt;
&lt;li&gt;Maximum number of concurrent positions&lt;/li&gt;
&lt;li&gt;Automatic pause on RPC or execution failures&lt;/li&gt;
&lt;li&gt;Separate hot wallet with only risk capital&lt;/li&gt;
&lt;li&gt;Clear kill switch (Telegram command or simple flag file)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most accounts that blow up do so because of position sizing and lack of forced exits, not because the entry signals were terrible.&lt;/p&gt;




&lt;h2&gt;
  
  
  Putting It Together — A Minimal Viable Strategy
&lt;/h2&gt;

&lt;p&gt;A realistic starting point many developers use:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Ingest new pairs + social mentions&lt;/li&gt;
&lt;li&gt;Apply the core safety filters listed earlier&lt;/li&gt;
&lt;li&gt;Require a minimum attention score&lt;/li&gt;
&lt;li&gt;Enter with small fixed SOL size via Jupiter&lt;/li&gt;
&lt;li&gt;Take 50% profit at +60–80%&lt;/li&gt;
&lt;li&gt;Trail the rest with a relatively tight stop&lt;/li&gt;
&lt;li&gt;Time-stop the entire position after 30–60 minutes if still open&lt;/li&gt;
&lt;li&gt;Log every decision and review weekly&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This will not make you rich by itself. It will, however, give you a clean baseline you can measure and improve.&lt;/p&gt;

&lt;p&gt;For a more complete implementation that already includes refined filters, scoring, and risk parameters, the framework available at &lt;a href="https://selar.com/60lw5u0623" rel="noopener noreferrer"&gt;https://selar.com/60lw5u0623&lt;/a&gt; is designed exactly for developers who want to skip the early trial-and-error phase.&lt;/p&gt;




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

&lt;p&gt;Memecoin trading rewards systems thinking more than gut feeling. The developers who last are the ones who:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Treat social data as a timing tool, not a truth source&lt;/li&gt;
&lt;li&gt;Obsess over on-chain filters&lt;/li&gt;
&lt;li&gt;Size positions conservatively&lt;/li&gt;
&lt;li&gt;Exit with rules instead of hope&lt;/li&gt;
&lt;li&gt;Continuously review what actually worked&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The tools and APIs available today make it realistic for a single developer to build a competent system. The hard part is maintaining discipline once real money is on the line.&lt;/p&gt;

&lt;p&gt;Build the signal layer. Build the filter layer. Build the risk layer. Only then worry about squeezing out the last bit of speed.&lt;/p&gt;

&lt;p&gt;If you want a battle-tested starting strategy that already combines these elements, you can find it here: &lt;a href="https://selar.com/60lw5u0623" rel="noopener noreferrer"&gt;https://selar.com/60lw5u0623&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now go build something robust.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Further reading &amp;amp; tools&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Helius, Birdeye, DexScreener, and Jupiter documentation&lt;/li&gt;
&lt;li&gt;Previous articles in this series: CCXT production bot &amp;amp; Solana sniper architecture&lt;/li&gt;
&lt;li&gt;&lt;a href="https://selar.com/60lw5u0623" rel="noopener noreferrer"&gt;Memecoin Trading Strategy Resource&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;



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

&lt;/div&gt;

</description>
      <category>webdev</category>
      <category>cryptocurrency</category>
      <category>memecoin</category>
      <category>python</category>
    </item>
    <item>
      <title>Building a Memecoin Sniper Bot on Solana with Python</title>
      <dc:creator>Fxm Brand</dc:creator>
      <pubDate>Mon, 31 Aug 2026 16:47:12 +0000</pubDate>
      <link>https://dev.to/fxmbrand/building-a-memecoin-sniper-bot-on-solana-with-python-2ccl</link>
      <guid>https://dev.to/fxmbrand/building-a-memecoin-sniper-bot-on-solana-with-python-2ccl</guid>
      <description>&lt;p&gt;Memecoin markets move in seconds. By the time a human sees a new pair on a Telegram channel or DexScreener, the first wave of buyers has already entered. That’s why developers build &lt;strong&gt;sniper bots&lt;/strong&gt; — automated systems that detect new liquidity, apply filters, and execute buys faster than manual trading allows.&lt;/p&gt;

&lt;p&gt;This guide walks you through building a practical, production-oriented memecoin sniper bot on Solana using Python. We’ll cover:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reliable RPC and WebSocket connections&lt;/li&gt;
&lt;li&gt;Detecting new pairs and liquidity events&lt;/li&gt;
&lt;li&gt;On-chain safety filters (mint authority, freeze authority, liquidity locks, holder distribution)&lt;/li&gt;
&lt;li&gt;Execution via Jupiter Aggregator (recommended) or direct Raydium&lt;/li&gt;
&lt;li&gt;Position sizing and hard risk limits&lt;/li&gt;
&lt;li&gt;Error handling, logging, and circuit breakers&lt;/li&gt;
&lt;li&gt;Deploying on a VPS with monitoring&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is &lt;strong&gt;not&lt;/strong&gt; a “get rich quick” script. Sniping is competitive, capital-intensive, and full of rugs. The goal here is a clean, maintainable foundation you can harden and extend.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Strategy note&lt;/strong&gt;: Infrastructure alone doesn’t make a profitable system. Entry filters, exit rules, sizing, and risk management matter more than raw speed. If you want a ready-made memecoin trading framework with tested logic, risk rules, and practical filters, check the resource here: &lt;a href="https://selar.com/60lw5u0623" rel="noopener noreferrer"&gt;Memecoin Trading Strategy&lt;/a&gt;. This article focuses on the bot architecture that supports such strategies.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Why Solana for Memecoin Sniping?
&lt;/h2&gt;

&lt;p&gt;Solana currently hosts the majority of high-velocity memecoin activity because of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Extremely low transaction fees&lt;/li&gt;
&lt;li&gt;Fast block times&lt;/li&gt;
&lt;li&gt;Rich ecosystem of AMMs (Raydium, Pump.fun, Meteora, etc.)&lt;/li&gt;
&lt;li&gt;Good aggregator support (Jupiter)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The trade-offs are real: RPC rate limits, network congestion during hype, and a constant stream of low-quality or malicious tokens. A production bot must handle all of that gracefully.&lt;/p&gt;




&lt;h2&gt;
  
  
  Project Setup
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;solana-memecoin-sniper
&lt;span class="nb"&gt;cd &lt;/span&gt;solana-memecoin-sniper
python &lt;span class="nt"&gt;-m&lt;/span&gt; venv venv
&lt;span class="nb"&gt;source &lt;/span&gt;venv/bin/activate
pip &lt;span class="nb"&gt;install &lt;/span&gt;solana solders httpx python-dotenv loguru tenacity pandas
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Optional but useful:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;anchorpy base58
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Recommended structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;solana-memecoin-sniper/
├── .env
├── config.py
├── bot.py
├── listener.py
├── filters.py
├── executor.py
├── risk.py
├── utils/
│   ├── logger.py
│   └── helpers.py
└── requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;.env&lt;/code&gt; example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RPC_URL=https://mainnet.helius-rpc.com/?api-key=YOUR_KEY
WS_URL=wss://mainnet.helius-rpc.com/?api-key=YOUR_KEY
PRIVATE_KEY=your_base58_private_key
JUPITER_API=https://quote-api.jup.ag/v6
TELEGRAM_BOT_TOKEN=...
TELEGRAM_CHAT_ID=...
MAX_SOL_PER_TRADE=0.5
MAX_OPEN_POSITIONS=3
SLIPPAGE_BPS=800
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Security warning&lt;/strong&gt;: Never commit your private key. Use a dedicated hot wallet with limited funds. Prefer hardware wallet + separate signing service for larger capital.&lt;/p&gt;




&lt;h2&gt;
  
  
  Connecting to Solana
&lt;/h2&gt;

&lt;p&gt;Use a high-quality RPC. Public endpoints will rate-limit you instantly during volume spikes. Popular paid options include Helius, QuickNode, Triton, and GenesysGo.&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;# config.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dotenv&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_dotenv&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;solders.keypair&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Keypair&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;solana.rpc.async_api&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AsyncClient&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;base58&lt;/span&gt;

&lt;span class="nf"&gt;load_dotenv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;RPC_URL&lt;/span&gt; &lt;span class="o"&gt;=&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;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;RPC_URL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;WS_URL&lt;/span&gt; &lt;span class="o"&gt;=&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;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;WS_URL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;PRIVATE_KEY&lt;/span&gt; &lt;span class="o"&gt;=&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;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PRIVATE_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;keypair&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Keypair&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base58&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;b58decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PRIVATE_KEY&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AsyncClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RPC_URL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Test the connection:&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;asyncio&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;solana.rpc.async_api&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AsyncClient&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AsyncClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RPC_URL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_balance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;keypair&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pubkey&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="n"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Detecting New Pairs &amp;amp; Liquidity Events
&lt;/h2&gt;

&lt;p&gt;There are several common approaches:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Listen to Raydium or Pump.fun program logs&lt;/strong&gt; via WebSocket&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Poll new token listings&lt;/strong&gt; from Birdeye, DexScreener, or Helius enhanced APIs&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Monitor specific pool creation instructions&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For a practical starting point, many bots combine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;WebSocket logs for speed&lt;/li&gt;
&lt;li&gt;REST confirmation + metadata for safety&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s a simplified listener pattern using logs:&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;# listener.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;solana.rpc.websocket_api&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;connect&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;loguru&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;

&lt;span class="n"&gt;RAYDIUM_AMM_PROGRAM&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;  &lt;span class="c1"&gt;# example
&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;listen_for_new_pools&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&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;WS_URL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;logs_subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;filter_&lt;/span&gt;&lt;span class="o"&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;mentions&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;RAYDIUM_AMM_PROGRAM&lt;/span&gt;&lt;span class="p"&gt;]},&lt;/span&gt;
            &lt;span class="n"&gt;commitment&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;confirmed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Listening for new pool events...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;while&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;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;recv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="c1"&gt;# Parse logs for initialize2 / pool creation patterns
&lt;/span&gt;                &lt;span class="c1"&gt;# This part requires careful log decoding
&lt;/span&gt;                &lt;span class="nf"&gt;process_log_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&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="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&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;WebSocket error: &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;await&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In practice you will also want:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Helius &lt;code&gt;transactionSubscribe&lt;/code&gt; or enhanced websockets&lt;/li&gt;
&lt;li&gt;Birdeye / DexScreener new-pairs endpoints as a secondary signal&lt;/li&gt;
&lt;li&gt;A short confirmation delay (1–3 blocks) before acting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Raw speed without filters is a fast way to buy rugs.&lt;/p&gt;




&lt;h2&gt;
  
  
  Safety Filters (The Real Edge)
&lt;/h2&gt;

&lt;p&gt;Most sniper bots lose money because they buy everything. Production systems apply strict pre-trade filters.&lt;/p&gt;

&lt;p&gt;Common high-value checks:&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;# filters.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;solders.pubkey&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Pubkey&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;check_mint_authority&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Return True if mint authority is revoked (safer).&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_account_info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Pubkey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mint&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="c1"&gt;# Parse mint account data – authority should be None
&lt;/span&gt;    &lt;span class="c1"&gt;# Implementation depends on token program layout
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;  &lt;span class="c1"&gt;# placeholder
&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;check_freeze_authority&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Return True if freeze authority is revoked.&lt;/span&gt;&lt;span class="sh"&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="c1"&gt;# placeholder
&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;check_liquidity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pool_address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;min_sol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;10.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Ensure minimum SOL (or USD) liquidity exists.&lt;/span&gt;&lt;span class="sh"&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;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;check_top_holders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_top10_pct&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;40.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Reject if top 10 holders own too much supply.&lt;/span&gt;&lt;span class="sh"&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;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;is_safe_token&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;checks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;check_mint_authority&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mint&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;check_freeze_authority&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mint&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;check_liquidity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;check_top_holders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mint&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="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;checks&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Additional filters worth adding later:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;LP locked or burned&lt;/li&gt;
&lt;li&gt;No high buy/sell tax (via simulation)&lt;/li&gt;
&lt;li&gt;Social presence / website (optional, slower)&lt;/li&gt;
&lt;li&gt;Contract age or renounced ownership patterns&lt;/li&gt;
&lt;li&gt;Blacklist of known deployer wallets&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These filters are where most of the edge lives. Blind sniping is usually negative EV.&lt;/p&gt;




&lt;h2&gt;
  
  
  Execution: Jupiter Aggregator (Recommended)
&lt;/h2&gt;

&lt;p&gt;Jupiter gives you best-price routing across many Solana DEXs with a clean API.&lt;/p&gt;

&lt;p&gt;Basic quote + swap flow:&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;# executor.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;solders.transaction&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;VersionedTransaction&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;solana.rpc.types&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TxOpts&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;loguru&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;

&lt;span class="n"&gt;JUPITER_QUOTE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://quote-api.jup.ag/v6/quote&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;JUPITER_SWAP&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://quote-api.jup.ag/v6/swap&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_quote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input_mint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;output_mint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;slippage_bps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="o"&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;inputMint&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;input_mint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;outputMint&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;output_mint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;amount&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;slippageBps&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;slippage_bps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;AsyncClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JUPITER_QUOTE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&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;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;execute_swap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_public_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&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;quoteResponse&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;userPublicKey&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user_public_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wrapAndUnwrapSol&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;AsyncClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JUPITER_SWAP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;swap_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;# Deserialize and sign
&lt;/span&gt;    &lt;span class="n"&gt;tx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;VersionedTransaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nf"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;swap_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;swapTransaction&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;  &lt;span class="c1"&gt;# base64 decode first in real code
&lt;/span&gt;    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# Sign with keypair and send
&lt;/span&gt;    &lt;span class="c1"&gt;# ... full signing + send_raw_transaction logic here
&lt;/span&gt;    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;success&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Swap submitted&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;tx&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Important production details:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Always simulate the transaction first when possible&lt;/li&gt;
&lt;li&gt;Use priority fees (compute unit price) during congestion&lt;/li&gt;
&lt;li&gt;Handle partial fills and failed transactions cleanly&lt;/li&gt;
&lt;li&gt;Track transaction signatures and confirm finality&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Direct Raydium instruction building is possible but more complex and usually worse priced than Jupiter.&lt;/p&gt;




&lt;h2&gt;
  
  
  Position Sizing &amp;amp; Risk Controls
&lt;/h2&gt;

&lt;p&gt;Never risk more than a small fixed percentage of your hot wallet per trade.&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;# risk.py
&lt;/span&gt;&lt;span class="n"&gt;MAX_SOL_PER_TRADE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&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;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MAX_SOL_PER_TRADE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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="n"&gt;MAX_OPEN_POSITIONS&lt;/span&gt; &lt;span class="o"&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;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MAX_OPEN_POSITIONS&lt;/span&gt;&lt;span class="sh"&gt;"&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="n"&gt;MAX_DAILY_LOSS_SOL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;2.0&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RiskManager&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;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;open_positions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;daily_pnl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;can_open_trade&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sol_amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&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;sol_amount&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;MAX_SOL_PER_TRADE&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;False&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;open_positions&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;MAX_OPEN_POSITIONS&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;False&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;daily_pnl&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;MAX_DAILY_LOSS_SOL&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;False&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;def&lt;/span&gt; &lt;span class="nf"&gt;record_open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;open_positions&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;record_close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pnl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;open_positions&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="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;open_positions&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;daily_pnl&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;pnl&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Additional hard rules many serious bots use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Maximum trades per hour&lt;/li&gt;
&lt;li&gt;Cooldown after a loss streak&lt;/li&gt;
&lt;li&gt;Automatic pause if RPC latency exceeds threshold&lt;/li&gt;
&lt;li&gt;Kill switch via Telegram command&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Main Bot Loop
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# bot.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;loguru&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;risk&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;RiskManager&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SniperBot&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;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;risk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;RiskManager&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;running&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Sniper bot starting...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# Start listener in background
&lt;/span&gt;        &lt;span class="n"&gt;listener_task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;listen_for_new_pools&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;running&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="c1"&gt;# In real implementation the listener pushes candidates
&lt;/span&gt;                &lt;span class="c1"&gt;# into an asyncio.Queue that we consume here
&lt;/span&gt;                &lt;span class="n"&gt;candidate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_next_candidate&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;candidate&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&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="k"&gt;continue&lt;/span&gt;

                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;is_safe_token&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;candidate&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mint&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;candidate&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pool&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
                    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&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;Rejected unsafe token: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;candidate&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;mint&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="sh"&gt;"&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;sol_amount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MAX_SOL_PER_TRADE&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;risk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;can_open_trade&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sol_amount&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Risk limits reached — skipping&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="k"&gt;continue&lt;/span&gt;

                &lt;span class="c1"&gt;# Get quote and execute
&lt;/span&gt;                &lt;span class="n"&gt;quote&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;get_quote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="n"&gt;input_mint&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;So11111111111111111111111111111111111111112&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# SOL
&lt;/span&gt;                    &lt;span class="n"&gt;output_mint&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;candidate&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mint&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                    &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&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;sol_amount&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;1e9&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;execute_swap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;keypair&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pubkey&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
                &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;risk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;record_open&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;success&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;Entered &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;candidate&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;mint&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="sh"&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="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exception&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="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&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="n"&gt;listener_task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cancel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Logging, Alerts &amp;amp; Monitoring
&lt;/h2&gt;

&lt;p&gt;Use structured logging and push critical events to Telegram:&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;from&lt;/span&gt; &lt;span class="n"&gt;loguru&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;

&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;INFO&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;logs/sniper_{time}.log&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rotation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;50 MB&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;retention&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;7 days&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;Telegram helper for fills, rejects, and errors is essential. You should also track:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;RPC latency&lt;/li&gt;
&lt;li&gt;Success vs failure rate of swaps&lt;/li&gt;
&lt;li&gt;Average entry slippage&lt;/li&gt;
&lt;li&gt;Daily realized P&amp;amp;L&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Deployment on a VPS
&lt;/h2&gt;

&lt;p&gt;Same principles as any production trading bot:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dedicated VPS close to your RPC provider when possible&lt;/li&gt;
&lt;li&gt;Docker or systemd&lt;/li&gt;
&lt;li&gt;Automatic restart&lt;/li&gt;
&lt;li&gt;Log rotation&lt;/li&gt;
&lt;li&gt;Separate hot wallet with limited SOL&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Docker example is almost identical to the previous CCXT article — just change the entrypoint and environment variables.&lt;/p&gt;

&lt;p&gt;During major memecoin launches, expect RPC and network congestion. Build in graceful degradation (pause new entries when latency spikes).&lt;/p&gt;




&lt;h2&gt;
  
  
  Realistic Expectations &amp;amp; Next Steps
&lt;/h2&gt;

&lt;p&gt;A basic sniper that only checks mint/freeze authority and minimum liquidity will still buy many losers. The difference between break-even and profitable usually comes from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Better filters (holder distribution, LP lock, tax simulation, deployer history)&lt;/li&gt;
&lt;li&gt;Position sizing and exit logic (trailing stops, time-based exits, partial takes)&lt;/li&gt;
&lt;li&gt;Capital discipline&lt;/li&gt;
&lt;li&gt;Continuous monitoring&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Speed helps, but filters and risk management matter more.&lt;/p&gt;

&lt;p&gt;The bot architecture in this article gives you a solid, extensible foundation. For a complete memecoin strategy layer — including entry rules, exit frameworks, and practical risk parameters — see the dedicated resource: &lt;a href="https://selar.com/60lw5u0623" rel="noopener noreferrer"&gt;https://selar.com/60lw5u0623&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Checklist Before Going Live
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Hot wallet only, limited funds&lt;/li&gt;
&lt;li&gt;[ ] All safety filters implemented and tested&lt;/li&gt;
&lt;li&gt;[ ] Simulation / dry-run mode works&lt;/li&gt;
&lt;li&gt;[ ] Telegram alerts for entries, exits, and errors&lt;/li&gt;
&lt;li&gt;[ ] Circuit breaker and daily loss limit active&lt;/li&gt;
&lt;li&gt;[ ] RPC and WebSocket reconnection logic tested&lt;/li&gt;
&lt;li&gt;[ ] Logs are being written and rotated&lt;/li&gt;
&lt;li&gt;[ ] You understand you can (and probably will) lose the entire hot-wallet balance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Start extremely small. Measure everything. Iterate on filters before increasing size.&lt;/p&gt;

&lt;p&gt;Happy building — and stay careful out there.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Useful resources&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://station.jup.ag/docs/apis/swap-api" rel="noopener noreferrer"&gt;Jupiter API Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.helius.dev" rel="noopener noreferrer"&gt;Helius Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/michaelhly/solana-py" rel="noopener noreferrer"&gt;Solana.py / Solders&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://selar.com/60lw5u0623" rel="noopener noreferrer"&gt;Memecoin Trading Strategy&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;



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

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

&lt;/div&gt;

</description>
      <category>python</category>
      <category>webdev</category>
      <category>memecoin</category>
      <category>cryptocurrency</category>
    </item>
    <item>
      <title>How to Build a Production-Ready Crypto Trading Bot (Spot + Futures) with CCXT</title>
      <dc:creator>Fxm Brand</dc:creator>
      <pubDate>Mon, 31 Aug 2026 16:33:45 +0000</pubDate>
      <link>https://dev.to/fxmbrand/how-to-build-a-production-ready-crypto-trading-bot-spot-futures-with-ccxt-8l2</link>
      <guid>https://dev.to/fxmbrand/how-to-build-a-production-ready-crypto-trading-bot-spot-futures-with-ccxt-8l2</guid>
      <description>&lt;p&gt;Building a crypto trading bot that actually survives real markets is harder than most tutorials admit. The difference between a weekend script and a production system comes down to reliability, risk controls, error handling, and clean architecture.&lt;/p&gt;

&lt;p&gt;In this guide we’ll build a solid foundation using &lt;strong&gt;CCXT&lt;/strong&gt; — the most widely used open-source library for connecting to cryptocurrency exchanges. You’ll learn how to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Connect to multiple CEXs with a unified API&lt;/li&gt;
&lt;li&gt;Trade both spot and futures&lt;/li&gt;
&lt;li&gt;Manage orders and positions properly&lt;/li&gt;
&lt;li&gt;Size positions safely&lt;/li&gt;
&lt;li&gt;Handle the messy realities of rate limits, network failures, and exchange quirks&lt;/li&gt;
&lt;li&gt;Deploy the bot on a VPS&lt;/li&gt;
&lt;li&gt;Add basic bridges toward DEX trading&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By the end you’ll have a maintainable, production-oriented skeleton you can extend for any strategy — including high-volatility memecoin approaches.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Quick note for memecoin traders&lt;/strong&gt;: If you’re specifically hunting for ready-to-use memecoin trading frameworks and strategies (sniping logic, risk filters, entry/exit rules), check out the practical system available here: &lt;a href="https://selar.com/60lw5u0623" rel="noopener noreferrer"&gt;Memecoin Trading Strategy&lt;/a&gt;. This article focuses on the robust bot infrastructure that any serious strategy needs underneath.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Why CCXT?
&lt;/h2&gt;

&lt;p&gt;CCXT gives you a single consistent interface across 100+ exchanges (Binance, Bybit, OKX, Gate, KuCoin, and many others). You write the same code for fetching balances, placing orders, or reading order books whether you’re on Binance Spot or Bybit Futures.&lt;/p&gt;

&lt;p&gt;Key advantages for production work:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unified method names and response shapes&lt;/li&gt;
&lt;li&gt;Built-in rate-limit handling (with &lt;code&gt;enableRateLimit&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Support for both REST and WebSocket (via &lt;code&gt;ccxt.pro&lt;/code&gt; for the paid/pro version, or community wrappers)&lt;/li&gt;
&lt;li&gt;Active maintenance and good documentation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For pure on-chain DEX work (Uniswap, Raydium, etc.) you’ll eventually need &lt;code&gt;web3.py&lt;/code&gt; or similar. CCXT can still help with CEX legs of hybrid strategies and some limited DEX support through certain connectors.&lt;/p&gt;




&lt;h2&gt;
  
  
  Project Setup
&lt;/h2&gt;

&lt;p&gt;Create a clean project structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;crypto-trading-bot
&lt;span class="nb"&gt;cd &lt;/span&gt;crypto-trading-bot
python &lt;span class="nt"&gt;-m&lt;/span&gt; venv venv
&lt;span class="nb"&gt;source &lt;/span&gt;venv/bin/activate   &lt;span class="c"&gt;# or venv\Scripts\activate on Windows&lt;/span&gt;
pip &lt;span class="nb"&gt;install &lt;/span&gt;ccxt python-dotenv pandas numpy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Recommended layout:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;crypto-trading-bot/
├── .env
├── config.py
├── bot.py
├── exchange.py
├── risk.py
├── utils/
│   ├── logger.py
│   └── helpers.py
├── strategies/
│   └── base.py
└── requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install additional useful packages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;python-telegram-bot loguru tenacity
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a &lt;code&gt;.env&lt;/code&gt; file (never commit this):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BINANCE_API_KEY=your_key_here
BINANCE_SECRET=your_secret_here
BYBIT_API_KEY=...
BYBIT_SECRET=...
TELEGRAM_BOT_TOKEN=...
TELEGRAM_CHAT_ID=...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Core Exchange Connector
&lt;/h2&gt;

&lt;p&gt;Here’s a clean, reusable exchange factory:&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;# exchange.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ccxt&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dotenv&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_dotenv&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;loguru&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;

&lt;span class="nf"&gt;load_dotenv&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;create_exchange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exchange_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;market_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;spot&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;
    market_type: &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;spot&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; or &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;future&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; / &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;swap&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;exchange_class&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ccxt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exchange_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&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;apiKey&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&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;getenv&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;exchange_id&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="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;_API_KEY&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;secret&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&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;getenv&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;exchange_id&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="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;_SECRET&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;enableRateLimit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;options&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;defaultType&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;market_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# critical for futures
&lt;/span&gt;        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;# Some exchanges need extra options
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;exchange_id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;binance&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;options&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;defaultType&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;market_type&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;market_type&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;future&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;swap&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;options&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;defaultType&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;future&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="n"&gt;exchange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;exchange_class&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Load markets once
&lt;/span&gt;    &lt;span class="n"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load_markets&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&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;Connected to &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;exchange_id&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;market_type&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&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="n"&gt;exchange&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Usage:&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="n"&gt;spot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_exchange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;binance&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;spot&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;futures&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_exchange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;binance&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;future&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;Always call &lt;code&gt;load_markets()&lt;/code&gt; early. It caches symbol information and helps avoid later surprises.&lt;/p&gt;




&lt;h2&gt;
  
  
  Fetching Market Data Safely
&lt;/h2&gt;

&lt;p&gt;Never assume the network or exchange is healthy. Wrap every call:&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;from&lt;/span&gt; &lt;span class="n"&gt;tenacity&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;retry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stop_after_attempt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;wait_exponential&lt;/span&gt;

&lt;span class="nd"&gt;@retry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stop&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;stop_after_attempt&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;wait&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;wait_exponential&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;multiplier&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="nb"&gt;min&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="nb"&gt;max&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&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;safe_fetch_ticker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&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="n"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch_ticker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@retry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stop&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;stop_after_attempt&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;wait&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;wait_exponential&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;multiplier&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="nb"&gt;min&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="nb"&gt;max&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&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;safe_fetch_ohlcv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeframe&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1m&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;limit&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="n"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch_ohlcv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeframe&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;timeframe&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Common data needs:&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="n"&gt;ticker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;safe_fetch_ticker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;spot&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;BTC/USDT&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="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;last&lt;/span&gt;&lt;span class="sh"&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bid&lt;/span&gt;&lt;span class="sh"&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ask&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="n"&gt;ohlcv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;safe_fetch_ohlcv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;spot&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ETH/USDT&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;5m&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# Convert to DataFrame if you want
&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="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="nc"&gt;DataFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ohlcv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;columns&lt;/span&gt;&lt;span class="o"&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;timestamp&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;open&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;high&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;low&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;close&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;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;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;timestamp&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;timestamp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;unit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ms&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;For futures you’ll often want funding rates, open interest, and mark price as well.&lt;/p&gt;




&lt;h2&gt;
  
  
  Order Management
&lt;/h2&gt;

&lt;p&gt;A production bot needs clear, idempotent order helpers.&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;place_market_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;side&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    side: &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;buy&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; or &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sell&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;
    amount: in base currency (e.g. BTC amount)
    &lt;/span&gt;&lt;span class="sh"&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;order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;market&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;side&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;side&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;success&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;Market &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;side&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; order placed: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;id&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="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;order&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="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&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;Order failed: &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;raise&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;place_limit_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;side&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&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="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;limit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;side&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;side&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;amount&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;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="ow"&gt;or&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;order&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;cancel_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&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="n"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cancel_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;symbol&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;get_open_orders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&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;exchange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch_open_orders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For futures, add leverage and margin mode:&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;set_leverage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;leverage&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="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_leverage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;leverage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&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;Leverage set to &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;leverage&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;x on &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;symbol&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;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="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warning&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;Could not set leverage: &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;def&lt;/span&gt; &lt;span class="nf"&gt;set_margin_mode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;isolated&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;  &lt;span class="c1"&gt;# or 'cross'
&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;exchange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_margin_mode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;symbol&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="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warning&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;Margin mode: &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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Always check the exchange-specific quirks. Binance Futures, Bybit, and OKX each have slightly different parameter names.&lt;/p&gt;




&lt;h2&gt;
  
  
  Position Sizing — The Most Important Part
&lt;/h2&gt;

&lt;p&gt;Most bot failures come from poor position sizing, not from bad signals.&lt;/p&gt;

&lt;p&gt;Here are three practical methods:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Fixed Fractional (Recommended starting point)
&lt;/h3&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;fixed_fractional_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;risk_percent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;entry_price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stop_loss_price&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Risk a fixed % of equity per trade.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;risk_amount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;risk_percent&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="n"&gt;price_diff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entry_price&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;stop_loss_price&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_diff&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="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;risk_amount&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;price_diff&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Volatility-based (ATR)
&lt;/h3&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;atr_position_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;risk_percent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;atr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;atr_multiplier&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;entry_price&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;risk_amount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;risk_percent&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="n"&gt;stop_distance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;atr&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;atr_multiplier&lt;/span&gt;
    &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;risk_amount&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;stop_distance&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Simple percentage of balance
&lt;/h3&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;percent_of_balance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;percent&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="n"&gt;notional&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;percent&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="n"&gt;notional&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Always enforce hard limits:&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="n"&gt;MAX_POSITION_PCT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.10&lt;/span&gt;          &lt;span class="c1"&gt;# never more than 10% of equity in one position
&lt;/span&gt;&lt;span class="n"&gt;MAX_LEVERAGE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="n"&gt;MAX_OPEN_POSITIONS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In a real bot you should also track current exposure across all positions.&lt;/p&gt;




&lt;h2&gt;
  
  
  Robust Error Handling &amp;amp; Resilience
&lt;/h2&gt;

&lt;p&gt;Exchanges will fail. Networks will drop. Rate limits will hit you. Your bot must survive.&lt;/p&gt;

&lt;p&gt;Key patterns:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Retries with exponential backoff&lt;/strong&gt; (tenacity is excellent)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Graceful degradation&lt;/strong&gt; — if one data source fails, try another or pause&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Circuit breakers&lt;/strong&gt; — stop trading after N consecutive errors&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Idempotency&lt;/strong&gt; — never place the same order twice by accident&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;State persistence&lt;/strong&gt; — save open orders and positions so a restart doesn’t lose context&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Example circuit breaker sketch:&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;class&lt;/span&gt; &lt;span class="nc"&gt;CircuitBreaker&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;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;failure_threshold&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;recovery_timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;failures&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;failure_threshold&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;failure_threshold&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recovery_timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;recovery_timeout&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;last_failure_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;closed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;  &lt;span class="c1"&gt;# closed, open, half-open
&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;record_failure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;failures&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;last_failure_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;failures&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;failure_threshold&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;open&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;critical&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Circuit breaker OPEN — trading paused&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;record_success&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;failures&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;closed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;can_execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;closed&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="bp"&gt;True&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;open&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;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;last_failure_time&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recovery_timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;half-open&lt;/span&gt;&lt;span class="sh"&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="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;  &lt;span class="c1"&gt;# half-open
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also handle specific CCXT exceptions:&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;ccxt&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;order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_order&lt;/span&gt;&lt;span class="p"&gt;(...)&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;ccxt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InsufficientFunds&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="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Not enough balance&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;ccxt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RateLimitExceeded&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="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Rate limit hit — backing off&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&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="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;ccxt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NetworkError&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="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Network issue&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;ccxt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ExchangeError&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="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&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;Exchange error: &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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Building the Main Bot Loop
&lt;/h2&gt;

&lt;p&gt;A simple but solid structure:&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;# bot.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;exchange&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;create_exchange&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;risk&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;calculate_position_size&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;utils.logger&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;setup_logger&lt;/span&gt;

&lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;setup_logger&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TradingBot&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;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exchange_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;binance&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;market_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;spot&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exchange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_exchange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exchange_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;market_type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;BTC/USDT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_running&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;circuit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CircuitBreaker&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;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bot started&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_running&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="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;circuit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;can_execute&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
                    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="k"&gt;continue&lt;/span&gt;

                &lt;span class="c1"&gt;# 1. Fetch data
&lt;/span&gt;                &lt;span class="n"&gt;ticker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;safe_fetch_ticker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch_balance&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

                &lt;span class="c1"&gt;# 2. Generate signal (your strategy here)
&lt;/span&gt;                &lt;span class="n"&gt;signal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate_signal&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="c1"&gt;# 3. Risk &amp;amp; size
&lt;/span&gt;                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;signal&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;buy&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;sell&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
                    &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;calculate_position_size&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;size&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;self&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="n"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

                &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;circuit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;record_success&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&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="c1"&gt;# adjust based on strategy
&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="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exception&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="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;circuit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;record_failure&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generate_signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&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="c1"&gt;# Replace with your real logic
&lt;/span&gt;        &lt;span class="c1"&gt;# For memecoin strategies this is where momentum, volume spikes,
&lt;/span&gt;        &lt;span class="c1"&gt;# social signals, or on-chain filters would live.
&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;def&lt;/span&gt; &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;side&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;place_market_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;side&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For higher frequency or multi-symbol bots, move to &lt;code&gt;asyncio&lt;/code&gt; + &lt;code&gt;ccxt.async_support&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Basic DEX Bridges
&lt;/h2&gt;

&lt;p&gt;CCXT is primarily CEX-focused. For real DEX work (especially memecoins on Solana or Ethereum):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;web3.py&lt;/code&gt; + Uniswap V2/V3 SDK or Raydium SDKs&lt;/li&gt;
&lt;li&gt;Or hybrid: use CCXT for the CEX leg and a separate module for on-chain execution&lt;/li&gt;
&lt;li&gt;Popular pattern: detect opportunity on-chain → route through a CEX if liquidity is better, or execute directly on DEX&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A minimal bridge sketch (Ethereum example):&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;from&lt;/span&gt; &lt;span class="n"&gt;web3&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Web3&lt;/span&gt;

&lt;span class="n"&gt;w3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Web3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Web3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;HTTPProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://mainnet.infura.io/v3/YOUR_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="c1"&gt;# You would then load the router ABI and call swapExactTokensForTokens
# This is significantly more complex than CCXT and requires careful gas,
# slippage, and nonce management.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For Solana memecoins the stack is different (solders, anchorpy, Jupiter aggregator, etc.). Many production memecoin bots combine CEX data feeds with on-chain execution.&lt;/p&gt;

&lt;p&gt;If your focus is memecoin-specific execution and risk frameworks, the strategy resource at &lt;a href="https://selar.com/60lw5u0623" rel="noopener noreferrer"&gt;https://selar.com/60lw5u0623&lt;/a&gt; is worth reviewing — it addresses the unique challenges of low-liquidity, high-speed environments that generic CEX bots often struggle with.&lt;/p&gt;




&lt;h2&gt;
  
  
  Deployment on a VPS
&lt;/h2&gt;

&lt;p&gt;Recommended starting setup:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DigitalOcean Droplet, Hetzner, or AWS Lightsail (2–4 GB RAM is usually enough for a single bot)&lt;/li&gt;
&lt;li&gt;Ubuntu 22.04 or 24.04&lt;/li&gt;
&lt;li&gt;Docker (strongly recommended)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Simple Docker approach
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Dockerfile&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; python:3.11-slim&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; requirements.txt .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-cache-dir&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["python", "bot.py"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;docker-compose.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3.8"&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;trading-bot&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
    &lt;span class="na"&gt;env_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.env&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./logs:/app/logs&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--build&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Alternative: systemd service
&lt;/h3&gt;

&lt;p&gt;Create &lt;code&gt;/etc/systemd/system/trading-bot.service&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[Unit]&lt;/span&gt;
&lt;span class="py"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Crypto Trading Bot&lt;/span&gt;
&lt;span class="py"&gt;After&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;network.target&lt;/span&gt;

&lt;span class="nn"&gt;[Service]&lt;/span&gt;
&lt;span class="py"&gt;User&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;ubuntu&lt;/span&gt;
&lt;span class="py"&gt;WorkingDirectory&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/home/ubuntu/crypto-trading-bot&lt;/span&gt;
&lt;span class="py"&gt;ExecStart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/home/ubuntu/crypto-trading-bot/venv/bin/python bot.py&lt;/span&gt;
&lt;span class="py"&gt;Restart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;always&lt;/span&gt;
&lt;span class="py"&gt;RestartSec&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;10&lt;/span&gt;
&lt;span class="py"&gt;EnvironmentFile&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/home/ubuntu/crypto-trading-bot/.env&lt;/span&gt;

&lt;span class="nn"&gt;[Install]&lt;/span&gt;
&lt;span class="py"&gt;WantedBy&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;multi-user.target&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl daemon-reload
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;trading-bot
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start trading-bot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Monitoring &amp;amp; Alerts
&lt;/h2&gt;

&lt;p&gt;Minimum viable monitoring:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Structured logging (loguru or structlog) → files + optional remote&lt;/li&gt;
&lt;li&gt;Telegram (or Discord) notifications for:

&lt;ul&gt;
&lt;li&gt;Order fills&lt;/li&gt;
&lt;li&gt;Errors / circuit breaker trips&lt;/li&gt;
&lt;li&gt;Daily P&amp;amp;L summary&lt;/li&gt;
&lt;li&gt;Heartbeat (bot is still alive)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Simple Telegram helper:&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;requests&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_telegram&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&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;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;TELEGRAM_BOT_TOKEN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;chat_id&lt;/span&gt; &lt;span class="o"&gt;=&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;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;TELEGRAM_CHAT_ID&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&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;https://api.telegram.org/bot&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/sendMessage&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&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;chat_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;chat_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;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;message&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also consider a simple health endpoint if you use Docker + a reverse proxy.&lt;/p&gt;




&lt;h2&gt;
  
  
  Security Checklist
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Use API keys with &lt;strong&gt;trading permissions only&lt;/strong&gt; (disable withdrawals)&lt;/li&gt;
&lt;li&gt;IP whitelist the VPS if the exchange supports it&lt;/li&gt;
&lt;li&gt;Never hard-code secrets&lt;/li&gt;
&lt;li&gt;Run the bot under a non-root user&lt;/li&gt;
&lt;li&gt;Keep dependencies updated&lt;/li&gt;
&lt;li&gt;Consider a separate “read-only” key for monitoring scripts&lt;/li&gt;
&lt;li&gt;Encrypt any local state files that contain sensitive data&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Going Further: Memecoin &amp;amp; High-Volatility Considerations
&lt;/h2&gt;

&lt;p&gt;Memecoins introduce extra challenges:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Extremely fast moves and thin order books&lt;/li&gt;
&lt;li&gt;Frequent contract changes / new pairs&lt;/li&gt;
&lt;li&gt;Higher chance of rugs and liquidity pulls&lt;/li&gt;
&lt;li&gt;Need for tighter circuit breakers and faster reaction times&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A production CEX bot (what we built above) is excellent for larger-cap pairs and as a reliable execution layer. For pure memecoin work you will usually combine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fast on-chain listeners&lt;/li&gt;
&lt;li&gt;Strict pre-trade filters (liquidity, holder distribution, mint/freeze authority, etc.)&lt;/li&gt;
&lt;li&gt;Very conservative position sizing&lt;/li&gt;
&lt;li&gt;Hard kill switches&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The infrastructure in this article gives you the reliable foundation. For battle-tested memecoin-specific strategy logic and risk frameworks, the resource at &lt;a href="https://selar.com/60lw5u0623" rel="noopener noreferrer"&gt;https://selar.com/60lw5u0623&lt;/a&gt; is designed exactly for that environment.&lt;/p&gt;




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

&lt;p&gt;You now have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clean exchange abstraction with CCXT&lt;/li&gt;
&lt;li&gt;Spot + Futures support&lt;/li&gt;
&lt;li&gt;Proper order helpers&lt;/li&gt;
&lt;li&gt;Position sizing primitives&lt;/li&gt;
&lt;li&gt;Retry &amp;amp; circuit-breaker patterns&lt;/li&gt;
&lt;li&gt;Deployment path (Docker or systemd)&lt;/li&gt;
&lt;li&gt;Monitoring hooks&lt;/li&gt;
&lt;li&gt;Security baseline&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Treat this as a solid skeleton, not a finished product. Real edge comes from your signal generation, risk rules, and continuous monitoring.&lt;/p&gt;

&lt;p&gt;Start small: paper trade or use very low size. Measure everything. Only increase capital when the system has proven stable for weeks.&lt;/p&gt;

&lt;p&gt;If you’re building specifically around memecoin opportunities, pair the infrastructure from this guide with a dedicated strategy layer — the one linked above is a strong starting point for many developers.&lt;/p&gt;

&lt;p&gt;Happy (and careful) building.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Useful links&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.ccxt.com" rel="noopener noreferrer"&gt;CCXT Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/ccxt/ccxt" rel="noopener noreferrer"&gt;CCXT GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://selar.com/60lw5u0623" rel="noopener noreferrer"&gt;Memecoin Trading Strategy Resource&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Questions or improvements? Drop them in the comments.&lt;/p&gt;



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

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

&lt;/div&gt;

</description>
      <category>cryptocurrency</category>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>How I Know My Trading Bot Is Actually Working (Without Staring at It All Day)</title>
      <dc:creator>Fxm Brand</dc:creator>
      <pubDate>Sat, 29 Aug 2026 23:34:21 +0000</pubDate>
      <link>https://dev.to/fxmbrand/how-i-know-my-trading-bot-is-actually-working-without-staring-at-it-all-day-nhb</link>
      <guid>https://dev.to/fxmbrand/how-i-know-my-trading-bot-is-actually-working-without-staring-at-it-all-day-nhb</guid>
      <description>&lt;p&gt;&lt;em&gt;Getting a trading bot's detection and execution logic right is only half the problem — the other half is knowing, at any moment, whether it's actually alive, healthy, and behaving normally, without babysitting a chart yourself. This post covers the observability layer: heartbeat checks, anomaly alerting on trade frequency and size, log discipline that actually helps during an incident, and the specific failure modes that are silent by default unless you build something to surface them.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The failure mode nobody designs for on day one
&lt;/h2&gt;

&lt;p&gt;Most write-ups about trading bots (including a couple of my own) focus on the interesting parts — signal detection, confluence scoring, execution logic. What they skip is the boring infrastructure question that actually determines whether you find out about a problem in five minutes or five days: how do you know, right now, whether your bot is working correctly?&lt;/p&gt;

&lt;p&gt;This matters more for a trading bot than most automated systems, because the cost of "it silently stopped working three days ago and I didn't notice" isn't a stale dashboard — it's either missed opportunity cost or, worse, a bot that's still running but behaving abnormally with real capital attached.&lt;/p&gt;




&lt;h2&gt;
  
  
  Layer 1: Is it even alive?
&lt;/h2&gt;

&lt;p&gt;The most basic check, and the one it's easy to assume you don't need until the day you do: a heartbeat.&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;time&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HeartbeatMonitor&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;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;webhook_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;interval_seconds&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;webhook_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;webhook_url&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;interval&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;interval_seconds&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;last_beat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&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;beat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;last_beat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&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;check_and_alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&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;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;last_beat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;interval&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="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_send_alert&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;No heartbeat in &lt;/span&gt;&lt;span class="si"&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;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;last_beat&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;s — bot may be down.&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;_send_alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;webhook_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&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;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;message&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This alone catches the crudest failure mode: the process crashed, the server rebooted and the service didn't restart, or a broker API outage hung a request indefinitely with no timeout. None of these are exotic scenarios — they're the ordinary failure modes of any long-running process, and a trading bot with no capital at risk while it's silently down is the good outcome. A trading bot that's silently &lt;em&gt;malfunctioning&lt;/em&gt; while still running is worse, which is why heartbeat alone isn't enough.&lt;/p&gt;




&lt;h2&gt;
  
  
  Layer 2: Is it behaving normally, not just running?
&lt;/h2&gt;

&lt;p&gt;A process can be technically alive while doing something wrong — stuck in a retry loop, placing far more trades than expected, or going unusually quiet during a session it should be active in. This requires baselining what "normal" looks like and alerting on deviation:&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;class&lt;/span&gt; &lt;span class="nc"&gt;AnomalyDetector&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;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expected_trades_per_session&lt;/span&gt;&lt;span class="o"&gt;=&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;4&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expected_range&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;expected_trades_per_session&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session_trade_count&lt;/span&gt; &lt;span class="o"&gt;=&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;record_trade&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session_trade_count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session_trade_count&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expected_range&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="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_alert&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;Trade count (&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session_trade_count&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;) is well above normal &lt;/span&gt;&lt;span class="sh"&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;range &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expected_range&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; — possible duplicate execution or logic error.&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;check_session_end&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session_trade_count&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Zero trades this session — check signal detection and broker connectivity.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session_trade_count&lt;/span&gt; &lt;span class="o"&gt;=&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;_alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&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;[ANOMALY] &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;message&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="c1"&gt;# replace with real alerting channel
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the layer that would have caught something like a webhook retry duplicating an order, or a confluence threshold silently misconfigured after an update — both of these are "the bot is technically running" failures, not "the bot crashed" failures, and a pure heartbeat check is blind to both.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Position size and exposure anomalies&lt;/strong&gt; deserve their own check, separate from trade count, because this is the category where an undetected bug is most expensive:&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;check_position_size_anomaly&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current_position_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expected_max_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;alert_fn&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;current_position_size&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;expected_max_size&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="nf"&gt;alert_fn&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;Position size &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;current_position_size&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; exceeds expected max &lt;/span&gt;&lt;span class="sh"&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;expected_max_size&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; by more than 50% — possible sizing bug.&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;h2&gt;
  
  
  Layer 3: Logs that actually help during an incident, not just after
&lt;/h2&gt;

&lt;p&gt;The instinct is to log everything. The reality is that undifferentiated logs are close to useless at 3am when something's actually wrong and you need to find the relevant line among thousands of routine ones. Structured, leveled logging with consistent fields matters more than log volume:&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;logging&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;

&lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getLogger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;goldmine_bot&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;log_trade_decision&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;decision&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;event&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;trade_decision&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;signal_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&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;confidence&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;confidence&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;decision&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;decision&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;# "executed" | "skipped"
&lt;/span&gt;        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reason&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;       &lt;span class="c1"&gt;# "below_threshold" | "risk_ceiling" | "executed"
&lt;/span&gt;        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;timestamp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;timestamp&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The specific discipline that pays off here: log every decision, not just executions. A gap in your logs where the bot should have evaluated a signal but didn't is often the first visible symptom of a real problem — and if you only log executed trades, that gap is invisible until you go looking for it, which usually means you're already troubleshooting a complaint rather than catching an issue proactively.&lt;/p&gt;




&lt;h2&gt;
  
  
  Layer 4: The dashboard question — what actually needs a human to see it live?
&lt;/h2&gt;

&lt;p&gt;Not everything needs a real-time dashboard. Most of what matters can be handled by alerting on deviation (layers 1–2) plus reviewing structured logs after the fact (layer 3). The genuinely useful real-time view tends to be narrow: current open positions and their unrealized P&amp;amp;L, time since last signal evaluation, and time since last successful broker API call. Anything beyond that starts turning into a distraction — a dashboard designed to be stared at tends to encourage exactly the manual-override temptation an automated system was supposed to remove in the first place.&lt;/p&gt;




&lt;h2&gt;
  
  
  What this actually caught in practice
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;A broker API rate limit that started silently dropping order confirmations&lt;/strong&gt; during a high-volatility news window — heartbeat stayed healthy (the process was fine), trade count looked plausible, but position-size reconciliation against the broker's actual account state caught a mismatch that wouldn't have surfaced any other way.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A logic change that quietly tightened the confluence threshold&lt;/strong&gt; further than intended during an update — the bot stayed alive and logged normally, but the zero-trades-this-session alert fired for three consecutive Asian sessions before anyone noticed, which is exactly the kind of gradual, non-crashing failure that observability layers 1–2 are built to catch and a human staring at a chart occasionally would likely miss.&lt;/p&gt;




&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Isn't this overkill for a single-strategy retail bot?&lt;/strong&gt;&lt;br&gt;
The heartbeat and zero-trade alerting are genuinely cheap to build and catch the most common failure modes — I'd consider those close to mandatory regardless of scale. The more granular anomaly detection can be added incrementally as you get a feel for which failure modes actually occur in your specific setup.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I set a reasonable "expected trades per session" baseline?&lt;/strong&gt;&lt;br&gt;
Start from your backtested or forward-tested signal frequency, add a reasonable margin, and adjust after a few weeks of real observed data — the goal is catching genuine anomalies, not generating so many false alerts that you start ignoring them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What alerting channel is actually best for this?&lt;/strong&gt;&lt;br&gt;
Whatever you'll actually see promptly — a webhook to a messaging app you already check (Slack, Discord, Telegram) tends to work better in practice than email, which is easy to let pile up unread.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Should logs and alerts be built before or after the trading logic itself?&lt;/strong&gt;&lt;br&gt;
Build the heartbeat and basic decision logging alongside the trading logic from the start — retrofitting observability after a bot has been running blind for months means you have no historical baseline for what "normal" ever looked like.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does more logging slow down execution-critical code paths?&lt;/strong&gt;&lt;br&gt;
Structured logging of decisions is cheap enough not to matter for typical trading frequencies, but if you're operating at very high frequency, asynchronous or buffered logging is worth considering so logging I/O doesn't sit in the critical execution path.&lt;/p&gt;




&lt;p&gt;If you run any long-lived automated system — trading or otherwise — what's the failure mode that was invisible until you specifically built something to detect it? I have a suspicion "the process is alive but doing something subtly wrong" is a more universal blind spot than most of us design for on the first pass.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>tutorial</category>
      <category>crypto</category>
    </item>
    <item>
      <title>The Timezone Bug That Breaks Every Session-Based Trading Strategy</title>
      <dc:creator>Fxm Brand</dc:creator>
      <pubDate>Sat, 29 Aug 2026 22:54:59 +0000</pubDate>
      <link>https://dev.to/fxmbrand/the-timezone-bug-that-breaks-every-session-based-trading-strategy-486j</link>
      <guid>https://dev.to/fxmbrand/the-timezone-bug-that-breaks-every-session-based-trading-strategy-486j</guid>
      <description>&lt;p&gt;&lt;em&gt;A session-based trading strategy lives or dies on knowing exactly when "the Asian session" or "the New York open" actually is — and that's a much harder problem than it sounds like, because your broker's server time, your platform's displayed time, and the real-world session boundaries are three different things that drift relative to each other, especially around daylight saving transitions. This post covers the actual bug patterns and the fix: never hardcode a session window in broker-server time, always normalize to a fixed reference (UTC/GMT), and treat DST as a first-class problem, not an edge case.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Why this bug is so easy to ship and so expensive to leave in production
&lt;/h2&gt;

&lt;p&gt;Session-based strategies — anything that says "trade the Asian session open" or "watch the London/New York handover" — depend entirely on correctly identifying when those windows actually occur. That sounds trivial until you notice that none of your obvious reference points agree with each other:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your broker's server time&lt;/strong&gt; is set by the broker, often in a timezone chosen for their own operational reasons, and is not guaranteed to be UTC, GMT, or your local time. Different brokers running the identical MT5 platform can have servers reporting different times for the exact same real-world moment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your platform's displayed candle time&lt;/strong&gt; is usually broker server time, not a global standard — which means a "15:00" candle on your chart isn't a fixed point in real-world time unless you know your specific broker's offset from UTC.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Real-world session boundaries&lt;/strong&gt; (Asian, London, New York) are defined relative to UTC/GMT and don't move — but they also don't align cleanly with any single broker's server time without a conversion step.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Daylight saving time&lt;/strong&gt; makes all of this worse, because it doesn't move in sync. The US, the EU, and your broker's server location can each shift DST on different calendar dates — and some brokers don't observe DST shifts on their server clock at all, meaning the broker-time-to-UTC offset itself changes twice a year and needs to be recalculated, not assumed constant.&lt;/p&gt;

&lt;p&gt;A strategy that hardcodes "Asian session = server time 00:00–08:00" will work correctly for exactly as long as the broker's offset from UTC stays constant — which is not indefinitely, and often not for very long.&lt;/p&gt;




&lt;h2&gt;
  
  
  The bug pattern, concretely
&lt;/h2&gt;

&lt;p&gt;Here's what this actually looks like in code that seems reasonable until you think about it:&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;# WRONG: assumes server time has a fixed, known relationship to UTC
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;is_asian_session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;candle_time&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;hour&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;candle_time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hour&lt;/span&gt;  &lt;span class="c1"&gt;# this is BROKER SERVER TIME, not UTC
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;hour&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will appear to work in testing — right up until a DST transition shifts the broker's real offset from UTC by an hour, silently shifting your "Asian session" window along with it. Nothing crashes. No error is thrown. The strategy just quietly starts evaluating the wrong hours as the session, and depending on how aggressively you're trading, this can go unnoticed for weeks.&lt;/p&gt;




&lt;h2&gt;
  
  
  The fix: normalize everything to a fixed reference before defining any session
&lt;/h2&gt;

&lt;p&gt;The core principle: session boundaries should be defined once, in UTC, and every timestamp you receive from your broker or platform needs to be converted to UTC before it's compared against those boundaries — never the reverse.&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;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="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timedelta&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pytz&lt;/span&gt;

&lt;span class="c1"&gt;# Define sessions ONCE, in UTC — this never changes regardless of broker
&lt;/span&gt;&lt;span class="n"&gt;SESSION_WINDOWS_UTC&lt;/span&gt; &lt;span class="o"&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;asian&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="mi"&gt;0&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;london&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="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;new_york&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="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;21&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;get_broker_utc_offset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;broker_timestamp_utc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;broker_timestamp_server&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 the CURRENT offset by comparing a known-good UTC timestamp
    (e.g. from an NTP-synced source or a UTC-timestamped tick) against
    what the broker&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s server reports for the same moment. Recompute
    this regularly — never cache it as a constant.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;delta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;broker_timestamp_server&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;broker_timestamp_utc&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;delta&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;server_time_to_utc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;server_time&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;current_offset&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;server_time&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;current_offset&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;candle_time_utc&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;hour&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;candle_time_utc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hour&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;SESSION_WINDOWS_UTC&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="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;hour&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;end&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;session&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The critical discipline here is &lt;strong&gt;recomputing the offset regularly rather than hardcoding it once&lt;/strong&gt;. A value that's correct today can silently become wrong twice a year without any code change on your end — the failure is entirely in the broker's own DST behavior changing underneath you.&lt;/p&gt;




&lt;h2&gt;
  
  
  Testing this properly
&lt;/h2&gt;

&lt;p&gt;The natural instinct is to unit test with a handful of example timestamps, but the actual bug surface is specifically at DST transition boundaries — so that's exactly where tests need to concentrate:&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;test_session_detection_across_dst_transition&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="c1"&gt;# Test a date just before a known DST transition and just after,
&lt;/span&gt;    &lt;span class="c1"&gt;# using real broker offset values captured on both sides
&lt;/span&gt;    &lt;span class="n"&gt;pre_dst_offset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;timedelta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hours&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="n"&gt;post_dst_offset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;timedelta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hours&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="n"&gt;server_time_pre&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2026&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="mi"&gt;28&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;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# before EU DST shift
&lt;/span&gt;    &lt;span class="n"&gt;server_time_post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2026&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="mi"&gt;30&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;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# after EU DST shift
&lt;/span&gt;
    &lt;span class="n"&gt;utc_pre&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;server_time_to_utc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;server_time_pre&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pre_dst_offset&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;utc_post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;server_time_to_utc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;server_time_post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;post_dst_offset&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Same server-time hour, different actual UTC hour and likely
&lt;/span&gt;    &lt;span class="c1"&gt;# different session — this is exactly the case that breaks
&lt;/span&gt;    &lt;span class="c1"&gt;# hardcoded server-time session windows
&lt;/span&gt;    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nf"&gt;get_session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;utc_pre&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nf"&gt;get_session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;utc_post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;utc_pre&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hour&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;utc_post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hour&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The point of a test like this isn't to assert a specific outcome — it's to make the DST-induced discrepancy &lt;em&gt;visible&lt;/em&gt; in a test run rather than discovering it live, three weeks after a transition, when your "Asian session" strategy has quietly been trading during London hours instead.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why this matters more for session-based strategies than almost any other kind
&lt;/h2&gt;

&lt;p&gt;A trend-following or indicator-based strategy that doesn't care about time of day is largely immune to this entire class of bug — it evaluates conditions continuously, and a shifted clock doesn't change what candle pattern is present. A session-based strategy is uniquely vulnerable because its entire premise depends on correctly bucketing time into windows that are defined in a reference frame (UTC) different from the one your data naturally arrives in (broker server time).&lt;/p&gt;

&lt;p&gt;This is also, not coincidentally, one of the most common real-world mistakes traders make when running any session-timed system manually or automated — getting the broker-time-to-session-window mapping wrong at setup, and then wondering why a well-validated strategy performs inconsistently in live conditions despite backtesting cleanly.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where this shows up in a real production system
&lt;/h2&gt;

&lt;p&gt;This exact class of bug is why the Goldmine Trading Bot's setup process includes explicit broker-time calibration rather than assuming a fixed offset — the session windows (Asian open, New York open) are defined in UTC internally, and the broker's current offset is calibrated during setup and treated as something that can drift, not a constant. Full disclosure: that's a product I build and sell, but the underlying lesson — normalize to a fixed reference before doing time-based comparisons, and never assume a timezone offset is stable — applies to any session-based system regardless of what platform or broker you're running it against.&lt;/p&gt;




&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Why doesn't my broker just report time in UTC to avoid all of this?&lt;/strong&gt;&lt;br&gt;
Some do, but many don't, often for legacy or regional operational reasons — and even brokers using a "UTC-like" server time don't always handle DST transitions identically, since some choose not to shift at all while others follow a specific regional DST calendar.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How often should I recompute my broker's UTC offset?&lt;/strong&gt;&lt;br&gt;
At minimum, around known DST transition dates for major regions (US, EU, UK) — recomputing it on every session start is a safe default that costs almost nothing computationally and eliminates an entire class of silent bugs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I just hardcode my broker's current offset once I've figured it out?&lt;/strong&gt;&lt;br&gt;
No — this is exactly the mistake this post is about. A correct offset today is not guaranteed to still be correct in a few months, and the failure mode is silent, not an error you'll notice immediately.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does this affect strategies that don't reference specific sessions at all?&lt;/strong&gt;&lt;br&gt;
Much less — a strategy with no time-of-day logic is largely unaffected, since it doesn't depend on correctly bucketing timestamps into named windows in the first place.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is there a library that handles broker-time-to-UTC conversion automatically?&lt;/strong&gt;&lt;br&gt;
Not universally, since the mapping is broker-specific and can change — most platforms require you to either query a server-time endpoint and compare it against a known UTC source, or handle it manually as shown above.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://selar.com/6744y9" rel="noopener noreferrer"&gt;Master The Gold Strategy I Used to Print Consistent Profit Every Asian Session. &lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;If you've run a time-sensitive automated system — trading or otherwise — against a data source with its own ambiguous or drifting clock, what was the failure mode that actually surfaced the bug for you? DST transitions specifically seem to be the recurring blind spot across a lot of different domains, not just trading.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>cryptocurrency</category>
      <category>algorithms</category>
    </item>
    <item>
      <title>Goldmine Trading Bot: Now $70 for a Limited Time — Automated XAUUSD Execution Without the 2am Screen Time</title>
      <dc:creator>Fxm Brand</dc:creator>
      <pubDate>Sat, 29 Aug 2026 22:51:07 +0000</pubDate>
      <link>https://dev.to/fxmbrand/goldmine-trading-bot-now-70-for-a-limited-time-automated-xauusd-execution-without-the-2am-3a0g</link>
      <guid>https://dev.to/fxmbrand/goldmine-trading-bot-now-70-for-a-limited-time-automated-xauusd-execution-without-the-2am-3a0g</guid>
      <description></description>
    </item>
    <item>
      <title>What Actually Separates a Retail Trading Algorithm That Survives From One That Doesn't</title>
      <dc:creator>Fxm Brand</dc:creator>
      <pubDate>Sat, 29 Aug 2026 22:28:30 +0000</pubDate>
      <link>https://dev.to/fxmbrand/what-actually-separates-a-retail-trading-algorithm-that-survives-from-one-that-doesnt-336c</link>
      <guid>https://dev.to/fxmbrand/what-actually-separates-a-retail-trading-algorithm-that-survives-from-one-that-doesnt-336c</guid>
      <description>&lt;p&gt;&lt;em&gt;Most retail algo trading write-ups focus on strategy ideas — the entry logic, the indicator combo. The part that actually determines whether a system survives contact with live markets is validation discipline and risk architecture, and that's the part most guides skip. This post covers the validation pipeline (in-sample → out-of-sample → walk-forward → paper trading → staged live deployment) and the risk architecture (drawdown-triggered position sizing, portfolio heat limits) that separates a system built to last from one that looks good in a single backtest.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The gap between "I have a strategy" and "I have a system"
&lt;/h2&gt;

&lt;p&gt;Retail algo trading has gotten genuinely accessible — you don't need institutional infrastructure or a quant PhD to run an automated strategy anymore. What hasn't gotten more accessible is the discipline required to know whether your strategy actually has an edge, or whether you've just curve-fit a backtest until it looked good.&lt;/p&gt;

&lt;p&gt;This is the gap that kills most retail algorithmic trading projects, and it has nothing to do with the sophistication of the entry logic. A three-parameter strategy validated properly will outlast a twenty-parameter strategy that was never stress-tested, every time.&lt;/p&gt;




&lt;h2&gt;
  
  
  The validation pipeline, stage by stage
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;In-sample development.&lt;/strong&gt; Build your strategy against historical data. The risk here isn't building the strategy — it's the temptation to keep adjusting parameters until the backtest looks good. Every adjustment made after seeing results is a small step toward curve-fitting noise instead of capturing a genuine, persistent edge.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Out-of-sample validation.&lt;/strong&gt; Test against data your development process never touched. This is the first real check: does the edge hold up on data it wasn't tuned against, or does performance collapse the moment it sees something new?&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;train_test_split_temporal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;train_ratio&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Never shuffle time-series data for a train/test split — that
    leaks future information into training. Split chronologically.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;split_idx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&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;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;train_ratio&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;data&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;split_idx&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;split_idx&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;Walk-forward analysis.&lt;/strong&gt; A single out-of-sample test isn't enough, because you've still only checked one specific historical period. Walk-forward validation rolls the train/test window forward repeatedly:&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;walk_forward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;window_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;step_size&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="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;while&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;window_size&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&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;data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;train&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="p"&gt;:&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;window_size&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&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;window_size&lt;/span&gt; &lt;span class="p"&gt;:&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;window_size&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="n"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;optimize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;train&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="nf"&gt;backtest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;))&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;step_size&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;aggregate&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The number worth trusting is the aggregate out-of-sample performance across every window — not the single best window, and not the in-sample result. If performance is wildly inconsistent window to window, the "edge" is likely fragile or regime-specific, not genuine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Monte Carlo simulation.&lt;/strong&gt; Randomize the sequence of your historical trades thousands of times and check whether performance holds up under different orderings:&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;random&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;monte_carlo_drawdown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trade_returns&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;simulations&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;max_drawdowns&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;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;simulations&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;shuffled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;trade_returns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shuffle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;shuffled&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;equity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;1.0&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;shuffled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;equity&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="n"&gt;equity&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;*&lt;/span&gt; &lt;span class="p"&gt;(&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;r&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;peak&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;equity&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;max_dd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;equity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;peak&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;peak&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;max_dd&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;max_dd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peak&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;peak&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;max_drawdowns&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="n"&gt;max_dd&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;max_drawdowns&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If a strategy's worst-case drawdown across simulated sequences is dramatically worse than what your single historical backtest showed, that backtest got lucky with trade ordering — and live trading won't reliably repeat that luck.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Paper trading.&lt;/strong&gt; Only after clearing the above does forward-testing with simulated capital make sense — this is where you actually discover execution slippage, API latency, and whether your strategy's own order flow moves the price you're trying to trade at.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Staged live deployment.&lt;/strong&gt; Start with a small fraction (10–20%) of intended capital, and scale only as live performance validates what the backtest and paper trading suggested. Every gap between expected and actual live performance is data worth logging, not just an annoyance to write off.&lt;/p&gt;

&lt;p&gt;Skipping stages doesn't just add risk evenly — it tends to hide exactly the failure mode most likely to blow up a live account, because backtests structurally can't see execution slippage or your own market impact, and a single out-of-sample test can't reveal regime fragility the way walk-forward analysis does.&lt;/p&gt;




&lt;h2&gt;
  
  
  Risk architecture: the part that determines whether you survive being wrong
&lt;/h2&gt;

&lt;p&gt;Here's the math worth internalizing before anything else: a 20% drawdown needs a 25% gain to recover. A 50% drawdown needs 100%. Risk architecture exists to keep you in the shallow end of that curve, because deep drawdowns don't just hurt — they mathematically cripple your ability to compound back to even.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Drawdown-triggered position sizing&lt;/strong&gt; is one of the more underused patterns in retail systems — rather than a fixed position size regardless of recent performance, size scales down as drawdown increases:&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;position_size_multiplier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current_drawdown_pct&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;current_drawdown_pct&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.20&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.0&lt;/span&gt;   &lt;span class="c1"&gt;# halt trading, mandatory review
&lt;/span&gt;    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;current_drawdown_pct&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.15&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="c1"&gt;# half size, mandatory strategy review
&lt;/span&gt;    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;current_drawdown_pct&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.10&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.75&lt;/span&gt;  &lt;span class="c1"&gt;# reduced size, increased selectivity
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This isn't punitive — it's a survival mechanism. A drawdown is market feedback about current conditions, and reducing exposure while you figure out whether conditions have genuinely changed is cheaper than finding out the hard way that they have.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Volatility-adjusted sizing&lt;/strong&gt; keeps risk exposure roughly constant even as market volatility changes — if volatility jumps 50%, position size should generally scale down to avoid a proportionally larger dollar swing per trade.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Portfolio heat control&lt;/strong&gt; matters even for a single-strategy retail system the moment you're running more than one instrument or timeframe simultaneously — correlated positions don't diversify risk, they quietly concentrate it, and a risk framework that only looks at position size per trade without checking cross-position correlation will understate real exposure during the exact market conditions where it matters most.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where this connects to a real production system
&lt;/h2&gt;

&lt;p&gt;Everything above is the same validation and risk discipline behind the Goldmine Trading Bot's structural signal engine — walk-forward validated confluence thresholds, and a defined-risk-before-entry model that calculates worst-case exposure per trade rather than adjusting it after the fact. If you've read the earlier breakdown of that engine's detection and scoring logic, this is the validation layer that sits underneath it, checking that the thresholds actually generalize rather than just fitting one convenient backtest window.&lt;/p&gt;

&lt;p&gt;Full disclosure: that's a product I build and sell. The validation pipeline and risk architecture in this post are general-purpose patterns worth using regardless of what strategy or instrument you're actually trading.&lt;/p&gt;




&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;How much historical data do I need for walk-forward validation to be meaningful?&lt;/strong&gt;&lt;br&gt;
Enough to cover multiple distinct market regimes (trending, ranging, high and low volatility) — a strategy validated only against one kind of market condition hasn't really been tested against the conditions most likely to break it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What's a reasonable profit factor or Sharpe ratio to target?&lt;/strong&gt;&lt;br&gt;
There's no universal number, but a profit factor consistently above 1.5 and a Sharpe ratio above 1.0 are commonly used as baseline viability thresholds for retail strategies — though these should be evaluated across walk-forward windows, not a single in-sample result.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is a 40% win rate with 2:1 reward-to-risk actually better than 60% win rate with 1:1?&lt;/strong&gt;&lt;br&gt;
Mathematically, yes, in terms of expected value — but the lower win rate version also means longer losing streaks that are statistically normal, not a sign something's broken, and a trader or system needs to be sized and psychologically prepared for that variance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why does Monte Carlo simulation matter if I already did walk-forward validation?&lt;/strong&gt;&lt;br&gt;
Walk-forward tests different historical time periods; Monte Carlo tests different possible orderings of the trades you already have. A strategy can pass walk-forward validation and still turn out to be fragile to trade sequencing — the two tests catch different failure modes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Should I build my own validation pipeline or use an existing platform's backtester?&lt;/strong&gt;&lt;br&gt;
Platforms like QuantConnect provide institutional-grade backtesting out of the box, which is often worth it purely to avoid subtly incorrect walk-forward or Monte Carlo implementations — a bug in your own validation code is one of the more dangerous places for an error to hide, since it can make a bad strategy look validated.&lt;/p&gt;




&lt;h2&gt;
  
  
  Discussion
&lt;/h2&gt;

&lt;p&gt;If you've deployed a retail trading system, what stage of this pipeline actually caught the problem that would have hurt you live — out-of-sample testing, walk-forward, Monte Carlo, or something paper trading revealed that no backtest could have shown? Curious which stage does the most real work in practice versus which one just feels rigorous.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>cryptocurrency</category>
      <category>algorithms</category>
    </item>
    <item>
      <title>How I Built a Structured XAUUSD Grid Trading System</title>
      <dc:creator>Fxm Brand</dc:creator>
      <pubDate>Sat, 29 Aug 2026 13:03:34 +0000</pubDate>
      <link>https://dev.to/fxmbrand/how-i-built-a-structured-xauusd-grid-trading-system-4pe0</link>
      <guid>https://dev.to/fxmbrand/how-i-built-a-structured-xauusd-grid-trading-system-4pe0</guid>
      <description>&lt;p&gt;Grid trading gets a bad reputation in trading dev communities, and honestly — it's earned. Search GitHub for "forex grid EA" and you'll find dozens of repos implementing naive martingale grids that look great on a 3-month backtest and catastrophic on a 3-year one. I wanted to build something different: a grid system for XAUUSD with hard risk boundaries, dynamic spacing, and a bias filter that shuts the whole thing down when the market stops ranging.&lt;/p&gt;

&lt;p&gt;Here's the architecture, the logic, and the mistakes I made getting there.&lt;/p&gt;

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

&lt;p&gt;A pure grid strategy is direction-agnostic by design — it profits from price oscillating through fixed levels, not from correctly calling a move. That's the appeal. The problem is that "direction-agnostic" also means "blind to trend," and gold trends hard when it wants to (think CPI surprises, risk-off flights to safety, central bank shocks). An uncapped grid on the wrong side of one of those moves doesn't just lose — it can compound losses if it's scaling position size on each new level.&lt;/p&gt;

&lt;p&gt;So the build had three non-negotiable requirements before a single order got placed:&lt;/p&gt;

&lt;p&gt;Exposure must be hard-capped, independent of how many grid levels are theoretically available.&lt;br&gt;
Grid spacing must be a function of current volatility, not a static constant.&lt;br&gt;
The system needs a "ranging vs. trending" classifier to decide whether the grid should even be active.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Component 1: The Ranging Classifier&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before deploying orders, the system checks higher-timeframe structure:&lt;/p&gt;

&lt;p&gt;Has price made a break of structure (BOS) in the last N bars on the H4?&lt;br&gt;
Is price currently contained within a recent swing high/low range (no expansion)?&lt;br&gt;
Has a liquidity sweep occurred recently without a confirmed follow-through move?&lt;/p&gt;

&lt;p&gt;If structure confirms ranging conditions, the grid is greenlit. If a fresh BOS or CHOCH fires, the classifier flags trending conditions, and the grid either shifts to a directionally-biased configuration or shuts down entirely, handing control to a directional entry model instead.&lt;/p&gt;

&lt;p&gt;This is the single biggest difference between this system and the martingale grids you'll find in most public repos — it doesn't run blind. It runs conditionally.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Component 2: Dynamic Spacing (ATR-Scaled)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Fixed pip spacing is the fastest way to make a grid strategy stop working the moment volatility regime shifts. I scaled grid spacing directly off a rolling ATR value:&lt;/p&gt;

&lt;p&gt;grid_spacing = ATR(period=14, timeframe=H1) * spacing_multiplier&lt;/p&gt;

&lt;p&gt;During low-volatility Asian session hours, this naturally tightens the grid. During high-volatility windows (London open, US data releases), it widens automatically — preventing the grid from getting chopped to pieces by noise that would otherwise trigger multiple levels in seconds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Component 3: Exposure Caps and the Kill Switch&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is where most public grid EAs fail, so it got the most engineering attention:&lt;/p&gt;

&lt;p&gt;Max concurrent grid levels: hard integer cap, not tied to available margin.&lt;br&gt;
Max total lot exposure: calculated as a fixed percentage of account equity, recalculated on every new level fill — not on a static starting balance.&lt;br&gt;
Drawdown kill switch: if floating drawdown on the grid cycle exceeds a defined threshold, the entire grid closes — win or lose — rather than letting it ride hoping for reversion.&lt;/p&gt;

&lt;p&gt;None of this is exotic engineering. It's just risk logic that a lot of grid implementations skip because it makes the backtest curve look less impressive.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the Backtests Actually Showed
&lt;/h2&gt;

&lt;p&gt;Backtesting across mixed regimes (a ranging month, a trending month, and one high-impact news week) showed the expected pattern: strong, consistent small gains during ranging conditions, near-zero activity during confirmed trending conditions (by design — the classifier shuts it down), and controlled, capped losses during the one week it misclassified an early-stage range as ongoing before a breakout occurred.&lt;/p&gt;

&lt;p&gt;That last case is the honest limitation of any grid system: the classifier isn't perfect, and there will be cycles where it's late to recognize a regime shift. The exposure cap exists specifically to make sure "late" costs a defined, small amount — not the account.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where This Fits Into a Broader XAUUSD System
&lt;/h2&gt;

&lt;p&gt;This grid module isn't meant to run standalone. It's built as one component of the broader Goldmine Strategy framework, which already handles the market structure and liquidity sweep logic used by the ranging classifier here. If you're building your own MQL5 or Python trading infrastructure for gold, treating grid trading as a conditional module — not a standalone strategy — is the difference between a system that survives multiple volatility regimes and one that gets liquidated the first time gold decides to trend for three weeks straight.&lt;/p&gt;

&lt;p&gt;If you want the full rule set this classifier is built on — the structure shift and liquidity sweep logic — that's documented in the Goldmine Strategy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sample Logic Flow
&lt;/h2&gt;

&lt;p&gt;For anyone thinking about implementing something similar, the high-level control flow looks roughly like this:&lt;/p&gt;

&lt;p&gt;on_new_bar():&lt;br&gt;
    regime = classify_regime(structure_data)&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if regime == TRENDING:
    close_all_grid_orders()
    return

atr = calculate_atr(period=14, timeframe=H1)
spacing = atr * spacing_multiplier

if current_exposure &amp;lt; max_exposure_cap:
    deploy_grid_levels(spacing, max_levels)

if floating_drawdown &amp;gt; kill_switch_threshold:
    close_all_grid_orders()
    halt_new_deployments(cooldown_period)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This is deliberately simplified, but it captures the important part: the risk governance checks (exposure cap, kill switch) run independently of whether the regime classifier thinks conditions are favorable. Nothing in the deployment logic can override the risk layer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing Gotchas Worth Flagging
&lt;/h2&gt;

&lt;p&gt;A few issues came up during testing that aren't obvious until you hit them:&lt;/p&gt;

&lt;p&gt;Spread modeling matters more than people assume. Early backtests used a fixed average spread, and results looked great. Switching to variable spread modeling — which widens automatically around news events, matching real broker behavior — knocked a meaningful chunk off the backtested returns. That's not a bug; that's the backtest becoming honest.&lt;/p&gt;

&lt;p&gt;Classifier whipsaw during transition periods. There were sequences where the regime classifier flipped between ranging and trending multiple times within a short window, right at the edge of a genuine structural shift. Each flip triggers a grid close/reopen cycle, which racks up spread cost if not handled carefully. Adding a small confirmation delay (requiring the new classification to hold for N bars before acting on it) reduced this without meaningfully hurting responsiveness.&lt;/p&gt;

&lt;p&gt;Broker-specific execution differences. An EA tested against one broker's historical tick data can behave differently on a live account with a different broker's execution model — slippage, requote behavior, and even how quickly pending orders fill can vary. Forward-testing on a demo account with your actual intended broker before going live isn't optional if you want the backtest numbers to mean anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  Open Questions for Further Iteration
&lt;/h2&gt;

&lt;p&gt;This system isn't a finished product — a few areas are worth continued work for anyone extending this kind of architecture: adaptive exposure caps that tighten automatically during elevated macro-event risk (rather than a single static percentage at all times), and a more granular regime classifier that outputs a confidence score rather than a binary ranging/trending flag, allowing grid spacing and exposure to scale smoothly with classifier confidence instead of switching abruptly at a threshold.&lt;/p&gt;

&lt;p&gt;If you're building similar infrastructure for gold or other volatile instruments, I'd genuinely be interested in comparing notes on regime classification approaches — it's the piece of this system that took the most iteration to get right, and it's also the piece most tutorials skip entirely in favor of just showing the entry/exit logic.&lt;/p&gt;

&lt;p&gt;If you'll love to get access to my grid trading system which has 95% win rate and consistent profit in indicator and ea bot&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://selar.com/5474cwo391" rel="noopener noreferrer"&gt;Get Access to The Goldmine Grid System&lt;/a&gt;
&lt;/h2&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>automation</category>
      <category>crypto</category>
    </item>
    <item>
      <title>Gold Grid Trading Strategy: XAUUSD Grid System Explained</title>
      <dc:creator>Fxm Brand</dc:creator>
      <pubDate>Sat, 29 Aug 2026 12:59:14 +0000</pubDate>
      <link>https://dev.to/fxmbrand/gold-grid-trading-strategy-xauusd-grid-system-explained-4jcg</link>
      <guid>https://dev.to/fxmbrand/gold-grid-trading-strategy-xauusd-grid-system-explained-4jcg</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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1781to2k2h4ohx5r709h.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1781to2k2h4ohx5r709h.png" alt=" " width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Gold doesn't move like other instruments. It trends hard, then chops sideways for days, then whipsaws through both directions in the same session. Most retail traders lose money on XAUUSD not because they can't read a chart, but because they're using a strategy built for trending assets on an instrument that spends half its life ranging. A grid trading system flips that weakness into an edge — instead of guessing direction, you let price come to you.&lt;/p&gt;

&lt;p&gt;This is a technical breakdown of how a grid system works on XAUUSD, why gold specifically suits this model, and where the Goldmine Strategy fits into a structured grid framework.&lt;/p&gt;

&lt;p&gt;**&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is Grid Trading, Really?
&lt;/h2&gt;

&lt;p&gt;**&lt;/p&gt;

&lt;p&gt;A grid strategy places a series of buy and sell orders at fixed price intervals above and below a reference price, forming a "grid." As price oscillates through the grid, positions get triggered, filled, and closed automatically — you're harvesting volatility rather than predicting a single directional move.&lt;/p&gt;

&lt;p&gt;On paper, it sounds like a "set and forget" dream. In practice, a grid without structure is a fast way to blow an account, because ungoverned grids don't know when to stop adding exposure in a strong trend. That's the part most YouTube tutorials skip.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why XAUUSD Is a Strong Candidate for Grid Systems
&lt;/h2&gt;

&lt;p&gt;Three characteristics make gold uniquely suited to grid-based entries:&lt;/p&gt;

&lt;p&gt;High average true range (ATR) — gold's daily range is typically $15–$30+, giving a grid enough room to fill multiple levels without needing extreme volatility events.&lt;br&gt;
Session-based mean reversion — Asian session gold behavior is notoriously range-bound before London and New York inject directional volume. Grids thrive in these compression windows.&lt;br&gt;
Deep liquidity — unlike thin altcoins or exotic pairs, XAUUSD has enough institutional volume that grid fills happen at predictable spreads, even during moderate volatility.&lt;/p&gt;

&lt;p&gt;The catch: gold is also prone to violent, news-driven directional runs (CPI, NFP, geopolitical shocks). A naive grid gets steamrolled here. This is exactly the gap a structured grid system — one with directional bias filters, not a symmetric "spray orders everywhere" grid — is built to close.&lt;/p&gt;

&lt;h2&gt;
  
  
  Anatomy of a Structured XAUUSD Grid
&lt;/h2&gt;

&lt;p&gt;A grid system that's actually survivable long-term needs four components:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;A bias filter. Before the grid deploys, higher-timeframe structure (H4/D1 trend, key supply/demand zones) determines whether the grid is symmetric (ranging bias) or skewed (trend bias, weighting one side with tighter spacing and larger take-profits).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Dynamic spacing. Fixed pip spacing is a rookie mistake on gold — a 200-pip grid spacing during a low-ATR week behaves completely differently than during an NFP week. Spacing should scale off ATR, not a static number.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Exposure caps. This is the single most important risk control in any grid system. A hard ceiling on total lot exposure and number of open grid levels prevents the classic failure mode: price trending hard against an uncapped grid until margin call.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Take-profit clustering, not martingale scaling. Many "grid EAs" quietly rely on martingale — doubling position size on each new level to average down. This is how accounts get liquidated in a single trending week. A structured grid keeps position sizing flat or modestly stepped, with defined max drawdown per grid cycle.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Where the Goldmine Strategy Fits
&lt;/h2&gt;

&lt;p&gt;The Goldmine Strategy was built around exactly this problem — gold's dual personality of range-bound compression followed by sharp breakout expansion. Rather than treating grid trading as a standalone system, it uses market structure shifts (breaks of structure, liquidity sweeps) to determine when a grid should be deployed versus when the market favors a directional breakout entry instead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In practice, that means:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;During Asian session consolidation, a tighter symmetric grid captures the back-and-forth chop.&lt;br&gt;
Once London liquidity sweeps a key high or low, the grid bias shifts to favor the breakout direction, with wider spacing to avoid getting chopped out on the initial fakeout.&lt;br&gt;
Exposure and drawdown limits are hard-coded, not discretionary — removing the temptation to "just add one more level" during a losing sequence.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Realistic Grid Trading Checklist
&lt;/h2&gt;

&lt;p&gt;Before running any grid system live on XAUUSD, confirm:&lt;/p&gt;

&lt;p&gt;Max total exposure is capped as a percentage of account equity, not open-ended.&lt;br&gt;
Grid spacing adjusts to current ATR, not a static pip value picked six months ago.&lt;br&gt;
There's a defined "kill switch" — a maximum drawdown level that closes the entire grid regardless of unrealized P&amp;amp;L.&lt;br&gt;
You've backtested across at least one high-volatility news week and one low-volatility ranging week, separately.&lt;br&gt;
Spread filters are in place — gold spreads widen fast around economic releases, and a grid firing into a 40-pip spread spike is a guaranteed loser.&lt;/p&gt;

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

&lt;p&gt;Grid trading on gold isn't a shortcut around doing analysis — it's a different way of expressing a view on volatility rather than direction. Done with proper exposure controls and session awareness, it can smooth out the emotional whiplash of trying to time every gold candle. Done without those controls, it's one strong NFP print away from disaster.&lt;/p&gt;

&lt;p&gt;If you want the exact rule set, spacing logic, and risk parameters used in a live-tested XAUUSD grid framework, the Goldmine Strategy breaks down the full system — structure shifts, liquidity confirmation, and grid deployment rules included.&lt;/p&gt;

&lt;p&gt;Manual vs. Automated Grid Execution&lt;/p&gt;

&lt;p&gt;You can run a grid strategy manually — placing pending orders by hand at calculated intervals — but in practice, this is one of the hardest strategy types to execute manually with any consistency. Grid systems depend on precise, repeated order placement across dozens of price levels, often across multiple sessions, and require constant recalculation of spacing as ATR shifts. A single missed level or a stale spacing calculation left over from a quieter week can throw off the entire structure.&lt;/p&gt;

&lt;p&gt;This is why most serious grid implementations are automated in MQL5 or a similar execution environment: the bot recalculates ATR-based spacing on a rolling basis, enforces exposure caps mechanically (removing the temptation to override them "just this once"), and executes the kill switch instantly the moment drawdown thresholds are breached — something a human watching multiple open positions across a volatile session will always be slower to act on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Questions About XAUUSD Grid Trading
&lt;/h2&gt;

&lt;p&gt;Does grid trading work in a strong trend? Not on its own, and it shouldn't be forced to. A well-built grid system includes a bias filter that recognizes trending conditions (via break of structure detection) and either shuts the grid down or shifts to a directionally-weighted configuration. Running a purely symmetric grid through a strong trend without this filter is the single most common cause of grid-related losses.&lt;/p&gt;

&lt;p&gt;How much capital do you need to run a grid strategy on gold? There's no fixed number, but grid strategies generally require more available margin headroom than a single-entry directional trade, since multiple levels can be open simultaneously. This is exactly why the exposure cap — a hard ceiling on total lot size regardless of how many levels are technically available — matters more in grid trading than almost any other strategy type.&lt;/p&gt;

&lt;p&gt;Is grid trading the same as martingale? No, though they're often confused. Martingale specifically refers to doubling (or otherwise scaling up) position size after a loss, in an attempt to recover previous losses with the next win. A structured grid can use flat or modestly stepped position sizing across levels — it doesn't require martingale scaling, and avoiding martingale is one of the clearest ways to keep a grid system's risk profile bounded and survivable.&lt;/p&gt;

&lt;p&gt;What timeframe is best for setting up a gold grid? Higher timeframes (H4/Daily) are typically used to classify the current regime (ranging vs. trending) and identify the broader range boundaries. Grid spacing itself is usually calculated off a shorter rolling ATR (commonly H1) so it stays responsive to current, not stale, volatility conditions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Word
&lt;/h2&gt;

&lt;p&gt;Grid trading isn't a magic bypass around doing real analysis on gold — it's a structured way of expressing a specific view: that price is more likely to oscillate within a defined range than to break decisively in either direction over the coming period. Get that classification right, respect the exposure caps, and a grid system can smooth out a meaningful share of the ranging hours that trip up purely directional gold strategies. Get the classification wrong without risk controls in place, and it's simply a slower way to give an account back to the market.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://selar.com/5474cwo391" rel="noopener noreferrer"&gt;Grab The Goldmine Grid System - Get Access to 95% Win Rate Trading System&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
