<?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: PyDev</title>
    <description>The latest articles on DEV Community by PyDev (@pydevtop).</description>
    <link>https://dev.to/pydevtop</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%2F3075399%2F8918375b-23a6-493a-b1e9-ae6c86f0c0d0.png</url>
      <title>DEV Community: PyDev</title>
      <link>https://dev.to/pydevtop</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/pydevtop"/>
    <language>en</language>
    <item>
      <title>Building a Production-Ready Risk Management Engine for Algorithmic Trading</title>
      <dc:creator>PyDev</dc:creator>
      <pubDate>Fri, 17 Jul 2026 12:39:43 +0000</pubDate>
      <link>https://dev.to/pydevtop/building-a-production-ready-risk-management-engine-for-algorithmic-trading-1jh8</link>
      <guid>https://dev.to/pydevtop/building-a-production-ready-risk-management-engine-for-algorithmic-trading-1jh8</guid>
      <description>&lt;p&gt;&lt;strong&gt;Part: 4 of 18&lt;/strong&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  About this series
&lt;/h1&gt;

&lt;p&gt;This series documents the engineering evolution of a production-ready algorithmic trading platform in Python. It focuses on architecture, state management, execution, real-time data processing, persistence, and the engineering decisions that transformed a simple trading bot into a production-ready platform.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;Part 3: Building a Production-Ready Position Manager for Algorithmic Trading&lt;/strong&gt;, I described the component responsible for maintaining persistent position state throughout the entire trade lifecycle.&lt;/p&gt;

&lt;p&gt;Read Part 3 here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/pydevtop/building-a-production-ready-position-manager-for-algorithmic-trading-55n4"&gt;https://dev.to/pydevtop/building-a-production-ready-position-manager-for-algorithmic-trading-55n4&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Position Manager could remember every open position.&lt;/p&gt;

&lt;p&gt;The next challenge was deciding what should happen to those positions as market conditions continuously changed.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Project Website&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This article is part of the engineering story behind the &lt;strong&gt;Bybit Signal Trading Platform&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you'd like to learn more about the project, see additional screenshots, features and technical details, visit:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://py-dev.top/application-software/bybit-signal-trading-bot"&gt;https://py-dev.top/application-software/bybit-signal-trading-bot&lt;/a&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  The Problem Was Never Stop Loss
&lt;/h1&gt;

&lt;p&gt;If someone had asked me during the first weeks of development where the Stop Loss logic should live, I wouldn't have hesitated.&lt;/p&gt;

&lt;p&gt;Inside the Trade Execution Engine.&lt;/p&gt;

&lt;p&gt;Where else?&lt;/p&gt;

&lt;p&gt;The engine already received TradingView webhooks.&lt;/p&gt;

&lt;p&gt;It validated incoming requests.&lt;/p&gt;

&lt;p&gt;It calculated Take Profit.&lt;/p&gt;

&lt;p&gt;It opened positions.&lt;/p&gt;

&lt;p&gt;Adding one more calculation felt completely natural.&lt;/p&gt;

&lt;p&gt;The implementation looked something like this.&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;signal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;receive_signal&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nf"&gt;validate&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;entry&lt;/span&gt; &lt;span class="o"&gt;=&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;signal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;stop_loss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;calculate_stop_loss&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;take_profit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;calculate_take_profit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Simple.&lt;/p&gt;

&lt;p&gt;Readable.&lt;/p&gt;

&lt;p&gt;Everything related to opening a trade existed in one place.&lt;/p&gt;

&lt;p&gt;At that moment there was absolutely no reason to introduce another component.&lt;/p&gt;

&lt;p&gt;There was only one trading pair.&lt;/p&gt;

&lt;p&gt;Only one open position.&lt;/p&gt;

&lt;p&gt;No persistence.&lt;/p&gt;

&lt;p&gt;No restart recovery.&lt;/p&gt;

&lt;p&gt;No Break Even.&lt;/p&gt;

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

&lt;p&gt;No Spot/Futures differences.&lt;/p&gt;

&lt;p&gt;No Reverse Signal.&lt;/p&gt;

&lt;p&gt;No Telegram controls.&lt;/p&gt;

&lt;p&gt;No analytics.&lt;/p&gt;

&lt;p&gt;The architecture matched the complexity of the platform.&lt;/p&gt;

&lt;p&gt;And that is exactly why it was dangerous.&lt;/p&gt;

&lt;p&gt;The biggest architectural mistakes rarely look like mistakes when a project is small.&lt;/p&gt;

&lt;p&gt;They usually look elegant.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why Simple Systems Lie
&lt;/h1&gt;

&lt;p&gt;One of the biggest traps in software engineering is believing that an architecture proven by a prototype is automatically suitable for production.&lt;/p&gt;

&lt;p&gt;It isn't.&lt;/p&gt;

&lt;p&gt;Prototype architectures optimize for speed.&lt;/p&gt;

&lt;p&gt;Production architectures optimize for change.&lt;/p&gt;

&lt;p&gt;Those are very different goals.&lt;/p&gt;

&lt;p&gt;During the first prototype almost every decision looked correct.&lt;/p&gt;

&lt;p&gt;Need another validation?&lt;/p&gt;

&lt;p&gt;Add another &lt;code&gt;if&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Need another parameter?&lt;/p&gt;

&lt;p&gt;Pass another argument.&lt;/p&gt;

&lt;p&gt;Need another calculation?&lt;/p&gt;

&lt;p&gt;Place it below the previous one.&lt;/p&gt;

&lt;p&gt;Nothing felt wrong because every new feature increased complexity by only a few lines of code.&lt;/p&gt;

&lt;p&gt;Then months passed.&lt;/p&gt;

&lt;p&gt;The project kept growing.&lt;/p&gt;

&lt;p&gt;The first partial Take Profit appeared.&lt;/p&gt;

&lt;p&gt;Soon after, Break Even was added.&lt;/p&gt;

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

&lt;p&gt;After that came Reverse Signals.&lt;/p&gt;

&lt;p&gt;Dry Run mode.&lt;/p&gt;

&lt;p&gt;Testnet.&lt;/p&gt;

&lt;p&gt;Mainnet.&lt;/p&gt;

&lt;p&gt;Spot trading.&lt;/p&gt;

&lt;p&gt;Futures trading.&lt;/p&gt;

&lt;p&gt;SQLite persistence.&lt;/p&gt;

&lt;p&gt;Restart recovery.&lt;/p&gt;

&lt;p&gt;Telegram control panel.&lt;/p&gt;

&lt;p&gt;WebSocket market data.&lt;/p&gt;

&lt;p&gt;Every feature looked completely independent.&lt;/p&gt;

&lt;p&gt;Every pull request looked small.&lt;/p&gt;

&lt;p&gt;But something interesting was happening.&lt;/p&gt;

&lt;p&gt;The execution engine was slowly becoming responsible for things that had nothing to do with execution.&lt;/p&gt;

&lt;p&gt;It started validating business rules.&lt;/p&gt;

&lt;p&gt;Tracking position state.&lt;/p&gt;

&lt;p&gt;Evaluating exposure.&lt;/p&gt;

&lt;p&gt;Reacting to market events.&lt;/p&gt;

&lt;p&gt;Updating persistent storage.&lt;/p&gt;

&lt;p&gt;Managing lifecycle transitions.&lt;/p&gt;

&lt;p&gt;Generating reports.&lt;/p&gt;

&lt;p&gt;Nothing broke.&lt;/p&gt;

&lt;p&gt;The architecture simply became harder to understand every month.&lt;/p&gt;

&lt;p&gt;That kind of failure is much more dangerous than an exception.&lt;/p&gt;

&lt;p&gt;Because nobody notices it immediately.&lt;/p&gt;




&lt;h1&gt;
  
  
  Execution Is Not Risk
&lt;/h1&gt;

&lt;p&gt;The turning point happened when I stopped looking at the code and started looking at the questions the platform was trying to answer.&lt;/p&gt;

&lt;p&gt;The trading strategy answered one question.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Should we open a position?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The execution engine answered another.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;How do we open the position?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then another question quietly appeared.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Are we still allowed to open this position?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;At first glance those questions seem related.&lt;/p&gt;

&lt;p&gt;Architecturally they are completely different.&lt;/p&gt;

&lt;p&gt;A trading signal is an opinion.&lt;/p&gt;

&lt;p&gt;Risk management is a policy.&lt;/p&gt;

&lt;p&gt;One predicts the market.&lt;/p&gt;

&lt;p&gt;The other protects the account.&lt;/p&gt;

&lt;p&gt;Mixing those responsibilities eventually creates a component that is difficult to extend because every new feature touches existing logic.&lt;/p&gt;

&lt;p&gt;Instead, the execution pipeline gradually evolved into something much cleaner.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TradingView Signal
        │
        ▼
Trade Execution Engine
        │
        ▼
Risk Evaluation
        │
   Approve / Reject
        │
        ▼
Position Manager
        │
        ▼
Exchange
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Separating those responsibilities simplified almost every other component.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Evolution of the Trade Execution Engine
&lt;/h1&gt;

&lt;p&gt;The first version of the execution engine was surprisingly small.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Receive Signal
      │
      ▼
Validate
      │
      ▼
Create Order
      │
      ▼
Exchange
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That architecture survived exactly until the platform became stateful.&lt;/p&gt;

&lt;p&gt;Once positions started living longer than a single order, everything changed.&lt;/p&gt;

&lt;p&gt;Now the engine needed information that wasn't available anymore.&lt;/p&gt;

&lt;p&gt;Questions appeared continuously.&lt;/p&gt;

&lt;p&gt;Is TP1 already reached?&lt;/p&gt;

&lt;p&gt;Is Break Even active?&lt;/p&gt;

&lt;p&gt;Did Trailing Stop move?&lt;/p&gt;

&lt;p&gt;How much quantity remains?&lt;/p&gt;

&lt;p&gt;Is this Spot or Futures?&lt;/p&gt;

&lt;p&gt;Has the application restarted?&lt;/p&gt;

&lt;p&gt;Is another position already open?&lt;/p&gt;

&lt;p&gt;None of those questions belonged to execution.&lt;/p&gt;

&lt;p&gt;They belonged to state.&lt;/p&gt;

&lt;p&gt;Or policy.&lt;/p&gt;

&lt;p&gt;Or lifecycle.&lt;/p&gt;

&lt;p&gt;Yet the execution engine knew about all of them.&lt;/p&gt;

&lt;p&gt;Every new responsibility increased coupling between components.&lt;/p&gt;

&lt;p&gt;Eventually changing one feature meant reading code completely unrelated to that feature.&lt;/p&gt;

&lt;p&gt;That was a clear indication that the architecture had reached its limits.&lt;/p&gt;




&lt;h1&gt;
  
  
  When "One More if" Became an Architectural Problem
&lt;/h1&gt;

&lt;p&gt;I don't remember the exact commit where the design became problematic.&lt;/p&gt;

&lt;p&gt;There wasn't one.&lt;/p&gt;

&lt;p&gt;Architectural debt rarely arrives in a single pull request.&lt;/p&gt;

&lt;p&gt;It grows one harmless decision at a time.&lt;/p&gt;

&lt;p&gt;It usually starts like this.&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;if&lt;/span&gt; &lt;span class="n"&gt;tp1_hit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;move_stop_loss&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A week later.&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;if&lt;/span&gt; &lt;span class="n"&gt;trailing_enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;update_trailing_stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another week.&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;if&lt;/span&gt; &lt;span class="n"&gt;reverse_signal&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;/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 python"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;dry_run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;simulate_order&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;execute_order&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;None of those conditions are incorrect.&lt;/p&gt;

&lt;p&gt;The problem is that every new condition teaches the component another responsibility.&lt;/p&gt;

&lt;p&gt;After dozens of iterations the execution engine wasn't executing anymore.&lt;/p&gt;

&lt;p&gt;It was becoming the platform itself.&lt;/p&gt;

&lt;p&gt;That violated one of the oldest principles in software architecture.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A component should have a single reason to change.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The solution was not deleting those conditions.&lt;/p&gt;

&lt;p&gt;The solution was asking a different question.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Which component should own this decision?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That question changed the architecture far more than any refactoring tool ever could.&lt;/p&gt;




&lt;h1&gt;
  
  
  Looking Beyond Stop Loss
&lt;/h1&gt;

&lt;p&gt;By this point it became obvious that Stop Loss had never been the real problem.&lt;/p&gt;

&lt;p&gt;Stop Loss was simply the first visible symptom.&lt;/p&gt;

&lt;p&gt;The real issue was ownership.&lt;/p&gt;

&lt;p&gt;Who owns trading state?&lt;/p&gt;

&lt;p&gt;Who owns execution?&lt;/p&gt;

&lt;p&gt;Who owns lifecycle?&lt;/p&gt;

&lt;p&gt;Who owns policy?&lt;/p&gt;

&lt;p&gt;Answering those questions eventually produced independent components that could evolve without constantly breaking each other.&lt;/p&gt;

&lt;p&gt;The Position Manager became responsible for remembering.&lt;/p&gt;

&lt;p&gt;The Trade Execution Engine returned to executing.&lt;/p&gt;

&lt;p&gt;And risk slowly became something much larger than a single Stop Loss calculation.&lt;/p&gt;

&lt;p&gt;In the next section we'll explore how position state fundamentally changed the entire risk model, and why introducing Break Even, Trailing Stop, and partial Take Profit forced the platform to think about risk as a continuously evolving process rather than a single calculation performed when a trade is opened.&lt;/p&gt;






&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart LR
    TV[TradingView Signal]
    TE[Trade Execution Engine]
    RM[Risk Evaluation]
    PM[Position Manager]
    EX[(Exchange)]

    TV --&amp;gt; TE
    TE --&amp;gt; RM
    RM --&amp;gt;|Approved| PM
    RM --&amp;gt;|Rejected| TE
    PM --&amp;gt; EX
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  Position Manager and Risk
&lt;/h1&gt;

&lt;p&gt;The previous chapter introduced the Position Manager as the component responsible for remembering the current state of every open position.&lt;/p&gt;

&lt;p&gt;At first glance, that doesn't sound related to risk management.&lt;/p&gt;

&lt;p&gt;It is.&lt;/p&gt;

&lt;p&gt;In fact, the Position Manager became the foundation that made meaningful risk evaluation possible.&lt;/p&gt;

&lt;p&gt;Without persistent position state, every incoming market update would have to be treated as if it were the first one.&lt;/p&gt;

&lt;p&gt;That isn't how real trading platforms work.&lt;/p&gt;

&lt;p&gt;Risk changes over time.&lt;/p&gt;

&lt;p&gt;The Position Manager tells us &lt;strong&gt;where the position currently is&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The Risk layer decides &lt;strong&gt;what should happen next&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Those responsibilities complement each other.&lt;/p&gt;

&lt;p&gt;They should never be merged.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                Position State
                       │
                       ▼
              Position Manager
                       │
        ┌──────────────┴──────────────┐
        ▼                             ▼
 Trade Execution Engine        Risk Evaluation
        │                             │
        └──────────────┬──────────────┘
                       ▼
                    Exchange
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One component remembers.&lt;/p&gt;

&lt;p&gt;The other evaluates.&lt;/p&gt;

&lt;p&gt;That distinction dramatically reduced coupling throughout the platform.&lt;/p&gt;




&lt;h1&gt;
  
  
  Break Even Changed Everything
&lt;/h1&gt;

&lt;p&gt;Initially Stop Loss was static.&lt;/p&gt;

&lt;p&gt;The position opened.&lt;/p&gt;

&lt;p&gt;The Stop Loss price was calculated.&lt;/p&gt;

&lt;p&gt;Nothing changed afterwards.&lt;/p&gt;

&lt;p&gt;That approach survived exactly until Break Even was implemented.&lt;/p&gt;

&lt;p&gt;Once the first Take Profit had been reached, the original Stop Loss no longer represented the platform's understanding of acceptable risk.&lt;/p&gt;

&lt;p&gt;The position had already realized profit.&lt;/p&gt;

&lt;p&gt;The remaining quantity was smaller.&lt;/p&gt;

&lt;p&gt;The maximum acceptable loss had changed.&lt;/p&gt;

&lt;p&gt;The Risk layer no longer asked:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Where is the Stop Loss?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Instead it asked:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What is the correct risk profile now?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is a much more interesting question.&lt;/p&gt;

&lt;p&gt;Break Even was not simply another price calculation.&lt;/p&gt;

&lt;p&gt;It represented a transition between two completely different lifecycle states.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Position Open
      │
      ▼
 Full Exposure
      │
      ▼
 TP1 Reached
      │
      ▼
 Break Even Activated
      │
      ▼
 Reduced Risk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Position Manager stored the transition.&lt;/p&gt;

&lt;p&gt;The Risk layer interpreted it.&lt;/p&gt;

&lt;p&gt;The execution engine simply followed instructions.&lt;/p&gt;

&lt;p&gt;Every component stayed focused on its own responsibility.&lt;/p&gt;




&lt;h1&gt;
  
  
  Trailing Stop Is Continuous Risk Evaluation
&lt;/h1&gt;

&lt;p&gt;Trailing Stop introduced another architectural shift.&lt;/p&gt;

&lt;p&gt;Unlike Break Even, which happens once, Trailing Stop continuously reacts to market movement.&lt;/p&gt;

&lt;p&gt;Every price update potentially changes the exit conditions.&lt;/p&gt;

&lt;p&gt;That meant risk was no longer evaluated only when a signal arrived.&lt;/p&gt;

&lt;p&gt;It also had to be evaluated while the position was already open.&lt;/p&gt;

&lt;p&gt;The platform gradually evolved from signal-driven processing into event-driven processing.&lt;/p&gt;

&lt;p&gt;Every market update became another opportunity to reduce exposure.&lt;/p&gt;

&lt;p&gt;The Position Manager remembered the highest price for long positions and the lowest price for short positions.&lt;/p&gt;

&lt;p&gt;The Risk layer compared those values with the configured trailing percentage.&lt;/p&gt;

&lt;p&gt;Only then could it determine whether the stop should move.&lt;/p&gt;

&lt;p&gt;Notice what did &lt;strong&gt;not&lt;/strong&gt; happen.&lt;/p&gt;

&lt;p&gt;The Trade Execution Engine never needed to know how the trailing calculation worked.&lt;/p&gt;

&lt;p&gt;It simply received a decision.&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;position&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;position_manager&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;symbol&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;decision&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;risk_manager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;evaluate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;current_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;decision&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;move_stop&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;execution_engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update_stop&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;new_stop&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That tiny separation made future changes significantly easier.&lt;/p&gt;

&lt;p&gt;When trailing logic changed, the execution engine remained untouched.&lt;/p&gt;

&lt;p&gt;When order execution changed, trailing logic remained untouched.&lt;/p&gt;

&lt;p&gt;The dependency became one-directional.&lt;/p&gt;

&lt;p&gt;Instead of every component understanding every rule, each component understood only its own domain.&lt;/p&gt;




&lt;h1&gt;
  
  
  Reverse Signals Are Risk Decisions
&lt;/h1&gt;

&lt;p&gt;Reverse Signals looked like an execution feature.&lt;/p&gt;

&lt;p&gt;Receive an opposite signal.&lt;/p&gt;

&lt;p&gt;Close the current position.&lt;/p&gt;

&lt;p&gt;Open another one.&lt;/p&gt;

&lt;p&gt;Simple.&lt;/p&gt;

&lt;p&gt;Until existing positions entered the picture.&lt;/p&gt;

&lt;p&gt;What happens if TP1 has already been reached?&lt;/p&gt;

&lt;p&gt;What if Trailing Stop is active?&lt;/p&gt;

&lt;p&gt;Should the platform immediately reverse?&lt;/p&gt;

&lt;p&gt;Or should it close the remaining quantity first?&lt;/p&gt;

&lt;p&gt;What if multiple markets behave differently?&lt;/p&gt;

&lt;p&gt;Those questions are not execution questions.&lt;/p&gt;

&lt;p&gt;They are risk questions.&lt;/p&gt;

&lt;p&gt;The execution engine should never decide whether abandoning a profitable position is acceptable.&lt;/p&gt;

&lt;p&gt;Its responsibility is simply to execute whatever decision has already been made.&lt;/p&gt;

&lt;p&gt;That realization simplified the architecture again.&lt;/p&gt;

&lt;p&gt;Instead of embedding more business rules inside order execution, Reverse Signal became another policy evaluated before execution.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Incoming Signal
       │
       ▼
Current Position
       │
       ▼
Risk Evaluation
       │
 Close?
 Ignore?
 Reverse?
       │
       ▼
Execution Engine
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As the platform matured, execution became progressively smaller.&lt;/p&gt;

&lt;p&gt;Policy became progressively smarter.&lt;/p&gt;

&lt;p&gt;That is exactly the direction healthy architectures tend to evolve.&lt;/p&gt;




&lt;h1&gt;
  
  
  Event-Driven Risk
&lt;/h1&gt;

&lt;p&gt;One of the most significant architectural improvements happened almost accidentally.&lt;/p&gt;

&lt;p&gt;Originally the platform reacted only to incoming TradingView webhooks.&lt;/p&gt;

&lt;p&gt;Eventually it reacted to events.&lt;/p&gt;

&lt;p&gt;That difference sounds subtle.&lt;/p&gt;

&lt;p&gt;It wasn't.&lt;/p&gt;

&lt;p&gt;Instead of repeatedly asking every component for information, the platform started broadcasting events.&lt;/p&gt;

&lt;p&gt;Examples included:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Position Opened&lt;/li&gt;
&lt;li&gt;Position Closed&lt;/li&gt;
&lt;li&gt;TP1 Reached&lt;/li&gt;
&lt;li&gt;Break Even Activated&lt;/li&gt;
&lt;li&gt;Trailing Stop Updated&lt;/li&gt;
&lt;li&gt;Stop Loss Triggered&lt;/li&gt;
&lt;li&gt;Reverse Signal Received&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every component reacted only to the events it actually understood.&lt;br&gt;
&lt;/p&gt;

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

Signal --&amp;gt; Execution

Execution --&amp;gt; PositionOpened

PositionOpened --&amp;gt; PositionManager

PositionOpened --&amp;gt; Risk

MarketPrice --&amp;gt; Risk

Risk --&amp;gt; TrailingUpdated

TrailingUpdated --&amp;gt; Execution

Execution --&amp;gt; Exchange
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The architecture became less dependent on direct function calls.&lt;/p&gt;

&lt;p&gt;Instead, components communicated through meaningful events.&lt;/p&gt;

&lt;p&gt;That reduced coupling and made future extensions considerably easier.&lt;/p&gt;

&lt;p&gt;Adding another lifecycle event no longer required modifying every existing component.&lt;/p&gt;

&lt;p&gt;Only interested components reacted.&lt;/p&gt;

&lt;p&gt;Everyone else ignored it.&lt;/p&gt;

&lt;p&gt;That is one of the strongest characteristics of production-ready event-driven systems.&lt;/p&gt;




&lt;h1&gt;
  
  
  Testing Strategy Changed Completely
&lt;/h1&gt;

&lt;p&gt;One unexpected benefit of separating responsibilities was testing.&lt;/p&gt;

&lt;p&gt;The earliest versions of the platform were difficult to test.&lt;/p&gt;

&lt;p&gt;Every new feature eventually required exchange connectivity.&lt;/p&gt;

&lt;p&gt;Eventually unit tests became integration tests.&lt;/p&gt;

&lt;p&gt;That slowed development considerably.&lt;/p&gt;

&lt;p&gt;After introducing dedicated responsibilities, testing became almost trivial.&lt;/p&gt;

&lt;p&gt;The Position Manager could be tested independently.&lt;/p&gt;

&lt;p&gt;The Risk layer could be tested independently.&lt;/p&gt;

&lt;p&gt;The execution engine could be mocked.&lt;/p&gt;

&lt;p&gt;Instead of building entire trading sessions, tests became deterministic.&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;position&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Position&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="mi"&gt;62000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;remaining_qty&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tp1_hit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;trailing_active&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;decision&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;risk_manager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;evaluate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;current_price&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;63250&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;assert&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;action&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;move_stop&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice what is missing.&lt;/p&gt;

&lt;p&gt;No exchange.&lt;/p&gt;

&lt;p&gt;No API credentials.&lt;/p&gt;

&lt;p&gt;No WebSocket connection.&lt;/p&gt;

&lt;p&gt;No TradingView.&lt;/p&gt;

&lt;p&gt;No market replay.&lt;/p&gt;

&lt;p&gt;Only deterministic input.&lt;/p&gt;

&lt;p&gt;Deterministic systems produce deterministic tests.&lt;/p&gt;

&lt;p&gt;That dramatically reduced debugging time and increased confidence before every release.&lt;/p&gt;

&lt;p&gt;Perhaps the biggest lesson wasn't about Stop Loss at all.&lt;/p&gt;

&lt;p&gt;It was about ownership.&lt;/p&gt;

&lt;p&gt;Once every component became responsible for only one problem, testing stopped being an obstacle and became another engineering tool.&lt;/p&gt;




&lt;h1&gt;
  
  
  Production Lessons
&lt;/h1&gt;

&lt;p&gt;Looking back, one of the biggest surprises wasn't discovering a better algorithm.&lt;/p&gt;

&lt;p&gt;It was discovering how little algorithms matter if the surrounding architecture cannot support them.&lt;/p&gt;

&lt;p&gt;Early in the project I spent far more time thinking about indicators than software design.&lt;/p&gt;

&lt;p&gt;EMA periods.&lt;/p&gt;

&lt;p&gt;RSI thresholds.&lt;/p&gt;

&lt;p&gt;MACD parameters.&lt;/p&gt;

&lt;p&gt;Take Profit percentages.&lt;/p&gt;

&lt;p&gt;Trailing distances.&lt;/p&gt;

&lt;p&gt;Those decisions certainly influence trading performance.&lt;/p&gt;

&lt;p&gt;But none of them determine whether the platform is maintainable six months later.&lt;/p&gt;

&lt;p&gt;Architecture does.&lt;/p&gt;

&lt;p&gt;The most valuable engineering lesson from this project wasn't learning how to calculate risk.&lt;/p&gt;

&lt;p&gt;It was learning how to organize it.&lt;/p&gt;

&lt;p&gt;Every production feature eventually forced another architectural decision.&lt;/p&gt;

&lt;p&gt;Partial Take Profit required persistent state.&lt;/p&gt;

&lt;p&gt;Break Even required lifecycle transitions.&lt;/p&gt;

&lt;p&gt;Trailing Stop required continuous evaluation.&lt;/p&gt;

&lt;p&gt;Reverse Signals required policy decisions.&lt;/p&gt;

&lt;p&gt;Analytics required historical persistence.&lt;/p&gt;

&lt;p&gt;Restart recovery required durable storage.&lt;/p&gt;

&lt;p&gt;None of those features were difficult individually.&lt;/p&gt;

&lt;p&gt;The challenge appeared when all of them needed to coexist without turning the codebase into a collection of special cases.&lt;/p&gt;

&lt;p&gt;The solution was never adding more conditions.&lt;/p&gt;

&lt;p&gt;The solution was reducing responsibilities.&lt;/p&gt;

&lt;p&gt;Every component gradually became smaller.&lt;/p&gt;

&lt;p&gt;The platform became larger.&lt;/p&gt;

&lt;p&gt;Ironically, those two things happened at the same time.&lt;/p&gt;




&lt;h1&gt;
  
  
  Engineering Means Preparing for Unknown Features
&lt;/h1&gt;

&lt;p&gt;One question appeared repeatedly during development.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Where should this feature go?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;At first the answer was almost always the same.&lt;/p&gt;

&lt;p&gt;Inside the Trade Execution Engine.&lt;/p&gt;

&lt;p&gt;Later the answer changed completely.&lt;/p&gt;

&lt;p&gt;Instead of asking where code should be added, the better question became:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Which component should own this responsibility?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That small change in mindset prevented many future architectural mistakes.&lt;/p&gt;

&lt;p&gt;A Position Manager should not execute orders.&lt;/p&gt;

&lt;p&gt;A Risk Engine should not send API requests.&lt;/p&gt;

&lt;p&gt;An Exchange Client should not evaluate trading policy.&lt;/p&gt;

&lt;p&gt;Analytics should never decide whether a position should close.&lt;/p&gt;

&lt;p&gt;Every component should be replaceable without rewriting the entire platform.&lt;/p&gt;

&lt;p&gt;That principle sounds obvious.&lt;/p&gt;

&lt;p&gt;It rarely remains obvious after months of continuous feature development.&lt;/p&gt;

&lt;p&gt;Good architecture isn't created once.&lt;/p&gt;

&lt;p&gt;It is protected continuously.&lt;/p&gt;




&lt;h1&gt;
  
  
  Future Evolution
&lt;/h1&gt;

&lt;p&gt;The current platform already supports production-oriented trading features.&lt;/p&gt;

&lt;p&gt;TradingView Webhooks.&lt;/p&gt;

&lt;p&gt;Spot and Futures execution.&lt;/p&gt;

&lt;p&gt;Partial Take Profit.&lt;/p&gt;

&lt;p&gt;Break Even.&lt;/p&gt;

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

&lt;p&gt;Reverse Signals.&lt;/p&gt;

&lt;p&gt;Persistent position storage.&lt;/p&gt;

&lt;p&gt;Analytics.&lt;/p&gt;

&lt;p&gt;Restart recovery.&lt;/p&gt;

&lt;p&gt;WebSocket market data.&lt;/p&gt;

&lt;p&gt;Telegram management.&lt;/p&gt;

&lt;p&gt;Yet the architecture intentionally leaves room for future growth.&lt;/p&gt;

&lt;p&gt;One possible direction is portfolio-level risk.&lt;/p&gt;

&lt;p&gt;Today every position is evaluated independently.&lt;/p&gt;

&lt;p&gt;A future version may evaluate the entire account before approving new trades.&lt;br&gt;
&lt;/p&gt;

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

 BTCUSDT ─────┐
 ETHUSDT ─────┤
 SOLUSDT ─────┤
              ▼
        Independent Risk


               Future

 BTCUSDT ─────┐
 ETHUSDT ─────┤
 SOLUSDT ─────┤
              ▼
 Portfolio Risk Engine
              │
 Exposure Limits
 Correlation Analysis
 Daily Loss Limits
 Sector Allocation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another future improvement involves adaptive position sizing.&lt;/p&gt;

&lt;p&gt;Today quantity is configured before trading begins.&lt;/p&gt;

&lt;p&gt;A future Risk Engine could determine position size dynamically based on volatility, available capital or portfolio exposure.&lt;/p&gt;

&lt;p&gt;Those improvements become possible because the architecture already separates execution from policy.&lt;/p&gt;

&lt;p&gt;Replacing one Risk implementation should not require changing the execution pipeline.&lt;/p&gt;

&lt;p&gt;That is exactly the flexibility modular systems are designed to provide.&lt;/p&gt;




&lt;h1&gt;
  
  
  Engineering Decisions That Changed the Platform
&lt;/h1&gt;

&lt;p&gt;Several decisions fundamentally changed the direction of the project.&lt;/p&gt;

&lt;p&gt;Not because they introduced new functionality.&lt;/p&gt;

&lt;p&gt;Because they simplified everything that followed.&lt;/p&gt;

&lt;h3&gt;
  
  
  State became explicit.
&lt;/h3&gt;

&lt;p&gt;Instead of reconstructing position state from exchange responses, the platform owns its own persistent state.&lt;/p&gt;

&lt;p&gt;That made restart recovery and analytics straightforward.&lt;/p&gt;




&lt;h3&gt;
  
  
  Risk became continuous.
&lt;/h3&gt;

&lt;p&gt;Instead of evaluating Stop Loss only when opening a trade, every market event became an opportunity to reassess exposure.&lt;/p&gt;

&lt;p&gt;Risk transformed from a calculation into an ongoing process.&lt;/p&gt;




&lt;h3&gt;
  
  
  Components became independent.
&lt;/h3&gt;

&lt;p&gt;Execution.&lt;/p&gt;

&lt;p&gt;Risk.&lt;/p&gt;

&lt;p&gt;Position Management.&lt;/p&gt;

&lt;p&gt;Analytics.&lt;/p&gt;

&lt;p&gt;Market Data.&lt;/p&gt;

&lt;p&gt;Exchange Connectivity.&lt;/p&gt;

&lt;p&gt;Each component now has a clearly defined responsibility.&lt;/p&gt;

&lt;p&gt;That dramatically reduced coupling across the platform.&lt;/p&gt;




&lt;h3&gt;
  
  
  Events became the language of the platform.
&lt;/h3&gt;

&lt;p&gt;Rather than allowing every module to call every other module directly, the system increasingly communicates through meaningful lifecycle events.&lt;/p&gt;

&lt;p&gt;Position Opened.&lt;/p&gt;

&lt;p&gt;TP1 Reached.&lt;/p&gt;

&lt;p&gt;Break Even Activated.&lt;/p&gt;

&lt;p&gt;Trailing Updated.&lt;/p&gt;

&lt;p&gt;Position Closed.&lt;/p&gt;

&lt;p&gt;Those events describe business activity instead of implementation details.&lt;/p&gt;

&lt;p&gt;That distinction makes the architecture easier to evolve.&lt;/p&gt;






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

Signal --&amp;gt; TradeEngine

TradeEngine --&amp;gt; Risk

Risk --&amp;gt; PositionManager

PositionManager --&amp;gt; Analytics

Risk --&amp;gt; Exchange

Exchange --&amp;gt; PositionManager

PositionManager --&amp;gt; Notifications

PositionManager --&amp;gt; Statistics

Analytics --&amp;gt; Reports
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As the number of features increased, communication patterns actually became simpler.&lt;/p&gt;

&lt;p&gt;That is usually a strong indication that an architecture is moving in the right direction.&lt;/p&gt;




&lt;h1&gt;
  
  
  Engineering Principle
&lt;/h1&gt;

&lt;p&gt;One idea became increasingly clear throughout the development of this trading platform.&lt;/p&gt;

&lt;p&gt;Risk management is not a function.&lt;/p&gt;

&lt;p&gt;It is not a Stop Loss formula.&lt;/p&gt;

&lt;p&gt;It is not a percentage.&lt;/p&gt;

&lt;p&gt;It is not even a single component.&lt;/p&gt;

&lt;p&gt;Risk management is a continuous conversation between market events, position state and business policy.&lt;/p&gt;

&lt;p&gt;The execution engine should never decide whether a position is safe.&lt;/p&gt;

&lt;p&gt;The Position Manager should never decide how an order is sent.&lt;/p&gt;

&lt;p&gt;The Exchange Client should never decide when exposure should change.&lt;/p&gt;

&lt;p&gt;Each component should answer one question exceptionally well.&lt;/p&gt;

&lt;p&gt;Everything else belongs somewhere else.&lt;/p&gt;

&lt;p&gt;That philosophy produced far cleaner code than any refactoring technique.&lt;/p&gt;

&lt;p&gt;The best architecture wasn't created by adding abstractions.&lt;/p&gt;

&lt;p&gt;It emerged by removing unnecessary responsibilities.&lt;/p&gt;

&lt;p&gt;As the platform grew larger, individual components became easier to understand.&lt;/p&gt;

&lt;p&gt;Paradoxically, that is often what healthy software evolution looks like.&lt;/p&gt;




&lt;h1&gt;
  
  
  What's Next
&lt;/h1&gt;

&lt;p&gt;In the next chapter we'll move one layer higher.&lt;/p&gt;

&lt;p&gt;So far we've focused on executing trades safely.&lt;/p&gt;

&lt;p&gt;The next challenge is understanding performance over time.&lt;/p&gt;

&lt;p&gt;We'll explore how the platform evolved from simple trade history into a production-ready analytics subsystem capable of measuring profitability, trading behavior, execution quality and long-term strategy performance.&lt;/p&gt;

&lt;p&gt;Because opening profitable trades is only half the problem.&lt;/p&gt;

&lt;p&gt;Understanding &lt;strong&gt;why&lt;/strong&gt; they were profitable is what turns a trading bot into an engineering platform.&lt;/p&gt;




&lt;h2&gt;
  
  
  Learn More
&lt;/h2&gt;

&lt;p&gt;This article covers only one part of the overall platform architecture.&lt;/p&gt;

&lt;p&gt;If you'd like to explore the project, its features and follow future development, visit the project page:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bybit Signal Trading Platform&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://py-dev.top/application-software/bybit-signal-trading-bot"&gt;https://py-dev.top/application-software/bybit-signal-trading-bot&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There you'll find an overview of the platform, supported features and future updates.&lt;/p&gt;




&lt;h2&gt;
  
  
  SEO Keywords Covered
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;algorithmic trading&lt;/li&gt;
&lt;li&gt;algorithmic trading platform&lt;/li&gt;
&lt;li&gt;Python trading bot&lt;/li&gt;
&lt;li&gt;Python trading architecture&lt;/li&gt;
&lt;li&gt;production-ready trading system&lt;/li&gt;
&lt;li&gt;production-ready risk management&lt;/li&gt;
&lt;li&gt;trading risk engine&lt;/li&gt;
&lt;li&gt;position manager&lt;/li&gt;
&lt;li&gt;trade execution engine&lt;/li&gt;
&lt;li&gt;TradingView webhook&lt;/li&gt;
&lt;li&gt;Bybit API&lt;/li&gt;
&lt;li&gt;WebSocket trading&lt;/li&gt;
&lt;li&gt;futures trading&lt;/li&gt;
&lt;li&gt;spot trading&lt;/li&gt;
&lt;li&gt;event-driven architecture&lt;/li&gt;
&lt;li&gt;software architecture&lt;/li&gt;
&lt;li&gt;trading platform architecture&lt;/li&gt;
&lt;li&gt;trading system design&lt;/li&gt;
&lt;li&gt;risk evaluation&lt;/li&gt;
&lt;li&gt;break even&lt;/li&gt;
&lt;li&gt;trailing stop&lt;/li&gt;
&lt;li&gt;reverse signal&lt;/li&gt;
&lt;li&gt;engineering blog&lt;/li&gt;
&lt;li&gt;backend engineering&lt;/li&gt;
&lt;li&gt;Python backend&lt;/li&gt;
&lt;li&gt;system design&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Thank you for reading! If you enjoyed this engineering story, follow the series as we continue documenting the architectural evolution of a production-ready algorithmic trading platform built with Python.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>trading</category>
      <category>bybit</category>
      <category>websockets</category>
    </item>
    <item>
      <title>Building a Production-Ready Position Manager for Algorithmic Trading</title>
      <dc:creator>PyDev</dc:creator>
      <pubDate>Wed, 15 Jul 2026 16:35:57 +0000</pubDate>
      <link>https://dev.to/pydevtop/building-a-production-ready-position-manager-for-algorithmic-trading-55n4</link>
      <guid>https://dev.to/pydevtop/building-a-production-ready-position-manager-for-algorithmic-trading-55n4</guid>
      <description>&lt;p&gt;&lt;em&gt;&lt;strong&gt;Series:&lt;/strong&gt; Engineering an Algorithmic Trading Platform in Python&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Part:&lt;/strong&gt; 3 of 18&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;About this series&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This series documents the engineering evolution of a production-ready algorithmic trading platform in Python. It focuses on architecture, state management, execution, real-time data processing, persistence, and the failures that forced the system to improve.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In &lt;a href="https://dev.to/pydevtop/designing-a-modular-trade-execution-engine-in-python-5ebh"&gt;Part 2: Designing a Modular Trade Execution Engine in Python&lt;/a&gt;, I described the component responsible for turning validated signals into controlled exchange operations.&lt;/p&gt;

&lt;p&gt;That engine could open a position.&lt;/p&gt;

&lt;p&gt;The next problem was keeping it alive.&lt;/p&gt;


&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;The first version of our trading engine did not have a Position Manager.&lt;/p&gt;

&lt;p&gt;It had variables.&lt;/p&gt;

&lt;p&gt;That distinction sounds trivial.&lt;/p&gt;

&lt;p&gt;It was not.&lt;/p&gt;

&lt;p&gt;The Trade Execution Engine received a TradingView webhook, validated the signal, submitted a market order, and stored a few values in memory.&lt;/p&gt;

&lt;p&gt;Then it waited for the next market price.&lt;/p&gt;

&lt;p&gt;The architecture felt clean because the first dozen test trades behaved exactly as expected:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TradingView Signal
        │
        ▼
Trade Execution Engine
        │
        ▼
Exchange Order
        │
        ▼
Position Closed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing appeared broken.&lt;/p&gt;

&lt;p&gt;The illusion lasted until the platform needed to manage something more complicated than a single entry followed by a single exit.&lt;/p&gt;

&lt;p&gt;Partial Take Profit arrived first.&lt;/p&gt;

&lt;p&gt;Suddenly one position had two quantities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the quantity already closed;&lt;/li&gt;
&lt;li&gt;the quantity still exposed to the market.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then Break Even appeared.&lt;/p&gt;

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

&lt;p&gt;Then reverse signals, Futures positions, leverage, restart recovery, Telegram controls, analytics, and multiple trading pairs.&lt;/p&gt;

&lt;p&gt;None of those features were individually difficult.&lt;/p&gt;

&lt;p&gt;The architecture was.&lt;/p&gt;

&lt;p&gt;The problem was no longer order execution.&lt;/p&gt;

&lt;p&gt;The problem was state.&lt;/p&gt;




&lt;p&gt;This Position Manager eventually became one of the core components of my Python algorithmic trading platform, responsible for managing both Spot and Futures positions throughout their entire lifecycle.&lt;/p&gt;

&lt;p&gt;The complete project is available here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://py-dev.top/application-software/bybit-signal-trading-bot"&gt;https://py-dev.top/application-software/bybit-signal-trading-bot&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The goal of this article isn't to explain the entire platform, but rather to document one architectural decision that significantly improved its reliability and maintainability.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Illusion of Simplicity
&lt;/h2&gt;

&lt;p&gt;Almost every small trading system begins with something like this:&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;entry_price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;62_480.5&lt;/span&gt;
&lt;span class="n"&gt;take_profit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;65_600.0&lt;/span&gt;
&lt;span class="n"&gt;stop_loss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;61_200.0&lt;/span&gt;
&lt;span class="n"&gt;qty&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.001&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For one position with one exit, these values are perfectly reasonable.&lt;/p&gt;

&lt;p&gt;Then the feature set grows.&lt;/p&gt;

&lt;p&gt;Partial Take Profit needs a remaining quantity:&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;remaining_qty&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.001&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Break Even needs another price and another transition flag:&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;breakeven_price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;62_480.5&lt;/span&gt;
&lt;span class="n"&gt;tp1_hit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Trailing Stop needs the best market extreme observed since entry:&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;highest_price&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;lowest_price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;62_480.5&lt;/span&gt;
&lt;span class="n"&gt;trailing_stop_price&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;trailing_active&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Futures introduce market type, side, and leverage:&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;market&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;futures&lt;/span&gt;&lt;span class="sh"&gt;"&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;short&lt;/span&gt;&lt;span class="sh"&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;10&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restart recovery needs timestamps and persistent storage.&lt;/p&gt;

&lt;p&gt;Analytics needs strategy, interval, signal source, order identifiers, fees, funding, and a close reason.&lt;/p&gt;

&lt;p&gt;Eventually the application is carrying values like these through unrelated functions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;entry_price
qty
remaining_qty
tp1_hit
tp2_hit
highest_price
lowest_price
breakeven_price
trailing_stop_price
market
side
leverage
created_at
updated_at
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing connects them except convention.&lt;/p&gt;

&lt;p&gt;Every function gains more parameters.&lt;/p&gt;

&lt;p&gt;Every feature updates state in several places.&lt;/p&gt;

&lt;p&gt;The code does not become more capable.&lt;/p&gt;

&lt;p&gt;It becomes more fragile.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Common pitfall&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A system can look modular at the class level while still sharing one large, implicit state model through loose variables.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The Real Problem Was State
&lt;/h2&gt;

&lt;p&gt;A position is not a group of prices.&lt;/p&gt;

&lt;p&gt;It is a business object with a history.&lt;/p&gt;

&lt;p&gt;Once the platform became event-driven, nearly every subsystem depended on the same evolving position state:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;signals opened positions;&lt;/li&gt;
&lt;li&gt;WebSocket prices modified them;&lt;/li&gt;
&lt;li&gt;Take Profit reduced them;&lt;/li&gt;
&lt;li&gt;Break Even changed their risk profile;&lt;/li&gt;
&lt;li&gt;Trailing Stop followed market extremes;&lt;/li&gt;
&lt;li&gt;reverse signals closed them;&lt;/li&gt;
&lt;li&gt;analytics interpreted their completed lifecycle;&lt;/li&gt;
&lt;li&gt;Telegram displayed their current state;&lt;/li&gt;
&lt;li&gt;persistence reconstructed them after restart.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keeping this information scattered across variables was no longer viable.&lt;/p&gt;

&lt;p&gt;The question changed from:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Which variables describe this trade?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;to:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What is a position in this system?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That question produced the Position Manager.&lt;/p&gt;




&lt;h2&gt;
  
  
  State Belongs Together
&lt;/h2&gt;

&lt;p&gt;Every active trade became one structured object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Position
│
├── Symbol
├── Market
├── Side
├── Original Quantity
├── Remaining Quantity
├── Entry Price
├── TP1 Price
├── TP2 Price
├── Stop Loss Price
├── Break-Even Price
├── Trailing Stop Price
├── Highest Observed Price
├── Lowest Observed Price
├── TP1 State
├── TP2 State
├── Trailing State
├── Leverage
├── Strategy Metadata
├── Execution Identifiers
├── Created At
└── Updated At
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A simplified persistent representation looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"symbol"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"BTCUSDT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"market"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"futures"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"side"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"short"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"entry_price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;62480.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"qty"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.001&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"remaining_qty"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.001&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tp1_price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;59356.475&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tp2_price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;56232.45&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"stop_loss_price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;65604.525&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"breakeven_price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;62480.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tp1_hit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tp2_hit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"trailing_active"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"highest_price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"lowest_price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;62480.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"leverage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"open"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Position Manager no longer remembers twenty unrelated values.&lt;/p&gt;

&lt;p&gt;It remembers one position.&lt;/p&gt;

&lt;p&gt;That object becomes the single source of truth for the active trade lifecycle.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Position Manager Stopped Making Trading Decisions
&lt;/h2&gt;

&lt;p&gt;A Position Manager can easily become a second Trade Execution Engine.&lt;/p&gt;

&lt;p&gt;It starts validating signals.&lt;/p&gt;

&lt;p&gt;Then it calculates indicators.&lt;/p&gt;

&lt;p&gt;Then it decides whether to enter.&lt;/p&gt;

&lt;p&gt;Then it calls the exchange.&lt;/p&gt;

&lt;p&gt;Soon no one can explain which component owns which decision.&lt;/p&gt;

&lt;p&gt;We moved in the opposite direction.&lt;/p&gt;

&lt;p&gt;The Trade Execution Engine owns trading intent.&lt;/p&gt;

&lt;p&gt;The Position Manager owns position state.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TradingView / Signal Engine
             │
             ▼
    Trade Execution Engine
             │
             ▼
       Position Manager
             │
             ▼
        State Changes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Trade Execution Engine answers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is this action supported?&lt;/li&gt;
&lt;li&gt;Is the pair enabled?&lt;/li&gt;
&lt;li&gt;Is execution allowed in the current mode?&lt;/li&gt;
&lt;li&gt;Should the action use Spot or Futures?&lt;/li&gt;
&lt;li&gt;Which exchange operation should be called?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Position Manager answers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What position currently exists?&lt;/li&gt;
&lt;li&gt;What changed after this price update?&lt;/li&gt;
&lt;li&gt;How much quantity remains?&lt;/li&gt;
&lt;li&gt;Which lifecycle transition is valid next?&lt;/li&gt;
&lt;li&gt;What state must be persisted?&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Engineering decision&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The Trade Execution Engine decides. The Position Manager remembers.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That boundary became one of the most important separations in the platform.&lt;/p&gt;




&lt;h2&gt;
  
  
  Orders Are Events; Positions Are State
&lt;/h2&gt;

&lt;p&gt;The distinction became obvious after the first partial exit.&lt;/p&gt;

&lt;p&gt;Imagine one Futures short position:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;An opening order creates the position.&lt;/li&gt;
&lt;li&gt;TP1 closes part of it.&lt;/li&gt;
&lt;li&gt;TP2 closes another part.&lt;/li&gt;
&lt;li&gt;Trailing Stop closes the remainder.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;How many positions existed?&lt;/p&gt;

&lt;p&gt;One.&lt;/p&gt;

&lt;p&gt;How many exchange orders existed?&lt;/p&gt;

&lt;p&gt;Four.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Position
│
├── Open Order
├── TP1 Close Order
├── TP2 Close Order
└── Final Close Order
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The position is the business object.&lt;/p&gt;

&lt;p&gt;Orders are events that mutate it.&lt;/p&gt;

&lt;p&gt;This also explains why exchange order history cannot replace application state. The exchange can report what executed, but it does not know:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;why TP1 existed;&lt;/li&gt;
&lt;li&gt;whether Break Even should activate afterward;&lt;/li&gt;
&lt;li&gt;whether TP2 closes a percentage of the original quantity or the remainder;&lt;/li&gt;
&lt;li&gt;when trailing is allowed to begin;&lt;/li&gt;
&lt;li&gt;which strategy opened the trade;&lt;/li&gt;
&lt;li&gt;what the next valid state transition should be.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The exchange remembers executions.&lt;/p&gt;

&lt;p&gt;The application remembers intent.&lt;/p&gt;




&lt;h2&gt;
  
  
  Price Updates Became Events
&lt;/h2&gt;

&lt;p&gt;Originally, every incoming WebSocket price executed trading logic directly.&lt;/p&gt;

&lt;p&gt;That was manageable when the platform had one Take Profit and one Stop Loss.&lt;/p&gt;

&lt;p&gt;It became difficult to reason about once TP1, TP2, Break Even, and Trailing Stop could all interact with the same position.&lt;/p&gt;

&lt;p&gt;We stopped treating a price as an instruction.&lt;/p&gt;

&lt;p&gt;A price became input to the current state.&lt;/p&gt;

&lt;p&gt;The output became a domain event.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Live Price
    │
    ▼
Current Position State
    │
    ▼
Position Manager
    │
    ▼
Domain Event
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A price update can produce an event such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tp1_hit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"symbol"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"BTCUSDT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"side"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"short"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;62836.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"close_qty"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.002&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"new_remaining_qty"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.002&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"new_stop_loss_price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;62712.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"new_lowest_price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;62712.0&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or it can produce a trailing update:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"trailing_update"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"symbol"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"BTCUSDT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"new_trailing_stop_price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;63150.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"new_lowest_price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;62500.0&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or a terminal event:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"stop_loss_hit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"symbol"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"BTCUSDT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"close_reason"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"stop_loss"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Position Manager does not need to place the order itself.&lt;/p&gt;

&lt;p&gt;It describes what changed.&lt;/p&gt;

&lt;p&gt;The execution layer performs the exchange operation, while storage persists the confirmed state.&lt;/p&gt;

&lt;p&gt;That distinction removed a surprising amount of coupling.&lt;/p&gt;




&lt;h2&gt;
  
  
  One Position Became Two Quantities
&lt;/h2&gt;

&lt;p&gt;Closing an entire position is straightforward.&lt;/p&gt;

&lt;p&gt;Partial closure is not.&lt;/p&gt;

&lt;p&gt;Suppose the platform opens a position with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Original Quantity: 0.004 BTC
Remaining Quantity: 0.004 BTC
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;TP1 closes half:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Closed at TP1:      0.002 BTC
Remaining Quantity: 0.002 BTC
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The position is now simultaneously:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;still open;&lt;/li&gt;
&lt;li&gt;partially realized;&lt;/li&gt;
&lt;li&gt;protected by a different Stop Loss;&lt;/li&gt;
&lt;li&gt;eligible for later lifecycle transitions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It cannot be represented accurately by a single &lt;code&gt;is_open&lt;/code&gt; Boolean.&lt;/p&gt;

&lt;p&gt;It has become a state machine.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;stateDiagram-v2
    [*] --&amp;gt; Open
    Open --&amp;gt; TP1Hit
    TP1Hit --&amp;gt; BreakEvenActive
    BreakEvenActive --&amp;gt; TP2Hit
    TP2Hit --&amp;gt; TrailingActive
    TrailingActive --&amp;gt; Closed
    Open --&amp;gt; Closed: Stop Loss
    BreakEvenActive --&amp;gt; Closed: Break Even
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every transition evolves the same position object.&lt;/p&gt;

&lt;p&gt;Nothing is deleted and recreated after TP1.&lt;/p&gt;

&lt;p&gt;The original context remains available:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;entry price;&lt;/li&gt;
&lt;li&gt;original quantity;&lt;/li&gt;
&lt;li&gt;remaining quantity;&lt;/li&gt;
&lt;li&gt;strategy;&lt;/li&gt;
&lt;li&gt;signal source;&lt;/li&gt;
&lt;li&gt;risk settings;&lt;/li&gt;
&lt;li&gt;execution history.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is critical for correct analytics and restart recovery.&lt;/p&gt;




&lt;h2&gt;
  
  
  Remaining Quantity Became the Most Important Number
&lt;/h2&gt;

&lt;p&gt;A partial exit changes future execution.&lt;/p&gt;

&lt;p&gt;After TP1, TP2 must not close the original quantity again.&lt;/p&gt;

&lt;p&gt;Trailing Stop must close only what remains.&lt;/p&gt;

&lt;p&gt;Analytics must distinguish realized and unrealized portions.&lt;/p&gt;

&lt;p&gt;The platform therefore updates &lt;code&gt;remaining_qty&lt;/code&gt; after every confirmed partial close:&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;new_remaining_qty&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="mf"&gt;0.0&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;remaining_qty&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;closed_qty&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update_position&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="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;remaining_qty&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;new_remaining_qty&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;updated_at&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="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 important detail is not the subtraction.&lt;/p&gt;

&lt;p&gt;It is when the subtraction becomes authoritative.&lt;/p&gt;

&lt;p&gt;The state should not be updated merely because the Position Manager predicted that TP1 had been reached. The execution operation must succeed first.&lt;/p&gt;

&lt;p&gt;The real sequence is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Price Reaches TP1
        │
        ▼
Position Manager Emits tp1_hit
        │
        ▼
Execution Layer Sends Partial Close
        │
        ▼
Exchange Confirms Operation
        │
        ▼
Persistent Position State Is Updated
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Otherwise the local platform can believe quantity was closed when the exchange rejected the request.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Lesson learned&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Predicted state and confirmed state are not the same thing.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Break Even Is Not Just a Price
&lt;/h2&gt;

&lt;p&gt;Most descriptions of Break Even reduce it to one operation:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Move Stop Loss to the entry price.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The calculation is easy.&lt;/p&gt;

&lt;p&gt;The state transition is not.&lt;/p&gt;

&lt;p&gt;Before TP1, the position accepts a configured maximum loss.&lt;/p&gt;

&lt;p&gt;After TP1, the platform enters a different risk state: part of the position has already been realized, and the remaining exposure should no longer be allowed to return to the original Stop Loss.&lt;/p&gt;

&lt;p&gt;For a long position:&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;new_stop_loss_price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;entry_price&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 a short position, the numeric value may be the same, but the trigger direction is reversed.&lt;/p&gt;

&lt;p&gt;The Position Manager therefore does not merely change a price.&lt;/p&gt;

&lt;p&gt;It records that the lifecycle has advanced:&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;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update_position&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="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tp1_hit&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;remaining_qty&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;new_remaining_qty&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stop_loss_price&lt;/span&gt;&lt;span class="sh"&gt;"&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;breakeven_price&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;updated_at&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="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;That state is then used by every future price update.&lt;/p&gt;

&lt;p&gt;Break Even is better understood as a risk transition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Before TP1
    │
    └── Configured Loss Is Still Possible

After TP1
    │
    └── Remaining Exposure Is Protected at Entry
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The difficult part was not calculating the Break-Even price.&lt;/p&gt;

&lt;p&gt;The difficult part was ensuring the platform could activate it exactly once and remember that decision after a restart.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Trailing Stop Is Actually a State Machine
&lt;/h2&gt;

&lt;p&gt;Trailing Stop is often described as a formula that follows price.&lt;/p&gt;

&lt;p&gt;That is only the final calculation.&lt;/p&gt;

&lt;p&gt;The harder questions are architectural:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When may trailing begin?&lt;/li&gt;
&lt;li&gt;Which quantity is still eligible?&lt;/li&gt;
&lt;li&gt;Which market extreme should be remembered?&lt;/li&gt;
&lt;li&gt;Can an older WebSocket price move the stop backward?&lt;/li&gt;
&lt;li&gt;What happens after restart?&lt;/li&gt;
&lt;li&gt;Has TP2 already occurred?&lt;/li&gt;
&lt;li&gt;Is this a long position or a short position?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a long position, the platform tracks the highest observed price:&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;if&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;highest_price&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="n"&gt;new_highest_price&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;For a short position, it tracks the lowest observed price:&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;if&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;lowest_price&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="n"&gt;new_lowest_price&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;The trailing price is derived from that extreme, not from the current tick.&lt;/p&gt;

&lt;p&gt;A short position might use:&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;new_trailing_stop_price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;new_lowest_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;trailing_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="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important invariant is monotonic protection.&lt;/p&gt;

&lt;p&gt;For a long position, the trailing stop must never move downward.&lt;/p&gt;

&lt;p&gt;For a short position, it must never move upward.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Long Position
    Highest Price ↑
    Trailing Stop  ↑ or unchanged

Short Position
    Lowest Price   ↓
    Trailing Stop  ↓ or unchanged
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is why trailing belongs to the position lifecycle rather than the exchange adapter. It depends on historical state, transition eligibility, remaining quantity, and the direction of the position.&lt;/p&gt;




&lt;h2&gt;
  
  
  Persistent State Stopped Being Optional
&lt;/h2&gt;

&lt;p&gt;During early testing, restarting the process cleared every in-memory position.&lt;/p&gt;

&lt;p&gt;That was acceptable while trades were synthetic.&lt;/p&gt;

&lt;p&gt;It stopped being acceptable once the exchange held real Testnet positions.&lt;/p&gt;

&lt;p&gt;One evening, the VPS restarted after automatic updates.&lt;/p&gt;

&lt;p&gt;Linux came back online.&lt;/p&gt;

&lt;p&gt;The platform started normally.&lt;/p&gt;

&lt;p&gt;The exchange still had open Futures positions.&lt;/p&gt;

&lt;p&gt;The application had forgotten every one of them.&lt;/p&gt;

&lt;p&gt;That was the moment persistence stopped being a useful feature.&lt;/p&gt;

&lt;p&gt;It became an operational requirement.&lt;/p&gt;

&lt;p&gt;Consider the failure sequence:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Position Opened
      │
      ▼
TP1 Executed
      │
      ▼
Stop Loss Moved to Break Even
      │
      ▼
Process Restarts
      │
      ▼
In-Memory State Is Lost
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without persistence, the platform no longer knows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;TP1 has already executed;&lt;/li&gt;
&lt;li&gt;half the quantity is gone;&lt;/li&gt;
&lt;li&gt;Stop Loss should remain at Break Even;&lt;/li&gt;
&lt;li&gt;trailing may be eligible later;&lt;/li&gt;
&lt;li&gt;the WebSocket subscription must be restored.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The exchange still has exposure, but the application has lost its strategy memory.&lt;/p&gt;

&lt;p&gt;Every meaningful state transition therefore has to be persisted.&lt;/p&gt;

&lt;p&gt;Not just position creation.&lt;/p&gt;

&lt;p&gt;Not just final closure.&lt;/p&gt;

&lt;p&gt;Every transition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Open Position       → Save
TP1 Confirmed       → Save
Break Even Applied  → Save
TP2 Confirmed       → Save
Trailing Updated    → Save
Position Closed     → Archive
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Engineering decision&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If losing a value changes future trading behaviour, that value belongs in persistent storage.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Recovery Is Reconstructing Behaviour, Not Reloading JSON
&lt;/h2&gt;

&lt;p&gt;Loading a position file after startup is easy.&lt;/p&gt;

&lt;p&gt;Restoring the platform is harder.&lt;/p&gt;

&lt;p&gt;A recovered position must become operational again.&lt;/p&gt;

&lt;p&gt;The restart flow 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;Application Starts
        │
        ▼
Load Open Positions
        │
        ▼
Validate Stored State
        │
        ▼
Restore WebSocket Subscriptions
        │
        ▼
Resume Price Processing
        │
        ▼
Continue from the Last Confirmed Lifecycle State
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The system must not replay TP1 if &lt;code&gt;tp1_hit&lt;/code&gt; is already true.&lt;/p&gt;

&lt;p&gt;It must not reset &lt;code&gt;remaining_qty&lt;/code&gt; to the original quantity.&lt;/p&gt;

&lt;p&gt;It must not discard the remembered highest or lowest price.&lt;/p&gt;

&lt;p&gt;It must not activate trailing before its prerequisites are satisfied.&lt;/p&gt;

&lt;p&gt;Recovery means reconstructing the same behaviour that existed before the process stopped.&lt;/p&gt;

&lt;p&gt;That is a much stricter requirement than simply reading stored data.&lt;/p&gt;




&lt;h2&gt;
  
  
  Position Ownership Became Explicit
&lt;/h2&gt;

&lt;p&gt;As the platform grew, more components needed access to position information:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the Trade Execution Engine;&lt;/li&gt;
&lt;li&gt;WebSocket processing;&lt;/li&gt;
&lt;li&gt;persistent storage;&lt;/li&gt;
&lt;li&gt;analytics;&lt;/li&gt;
&lt;li&gt;Telegram reporting.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Allowing every component to mutate positions directly would create an ownership problem.&lt;/p&gt;

&lt;p&gt;We introduced a simple rule:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Every position mutation flows through one controlled path.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                 +------------------------+
                 | Trade Execution Engine |
                 +-----------+------------+
                             |
                             v
                 +------------------------+
                 |    Position Manager    |
                 +-----------+------------+
                             |
             +---------------+----------------+
             |               |                |
             v               v                v
       Persistence      WebSocket State    Analytics Input
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Storage writes remain explicit.&lt;/p&gt;

&lt;p&gt;WebSocket callbacks provide prices but do not invent lifecycle transitions.&lt;/p&gt;

&lt;p&gt;Analytics reads completed state but does not modify active positions.&lt;/p&gt;

&lt;p&gt;Telegram displays state and triggers commands through the normal execution path.&lt;/p&gt;

&lt;p&gt;This ownership rule eliminated an entire class of synchronization problems.&lt;/p&gt;

&lt;p&gt;When a position changes, there is one place to investigate.&lt;/p&gt;




&lt;h2&gt;
  
  
  What the Position Manager Does Not Do
&lt;/h2&gt;

&lt;p&gt;Defining exclusions was as important as defining responsibilities.&lt;/p&gt;

&lt;p&gt;The Position Manager does not:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;generate TradingView signals;&lt;/li&gt;
&lt;li&gt;calculate EMA, RSI, or MACD;&lt;/li&gt;
&lt;li&gt;validate webhook secrets;&lt;/li&gt;
&lt;li&gt;choose Dry Run, Testnet, or Mainnet clients;&lt;/li&gt;
&lt;li&gt;format Telegram reports;&lt;/li&gt;
&lt;li&gt;calculate monthly trading statistics;&lt;/li&gt;
&lt;li&gt;communicate directly with every exchange endpoint.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It receives position state and a price.&lt;/p&gt;

&lt;p&gt;It returns a state transition.&lt;/p&gt;

&lt;p&gt;Conceptually, its core API is small:&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;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;position_manager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on_price_update&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="n"&gt;position&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;current_price&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 result is declarative:&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="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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tp1_hit&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_qty&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.002&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_remaining_qty&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.002&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_stop_loss_price&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;62712.0&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;This makes the component easier to test.&lt;/p&gt;

&lt;p&gt;A unit test does not need TradingView, Telegram, or a live Bybit connection. It only needs a position and a sequence of prices.&lt;/p&gt;




&lt;h2&gt;
  
  
  The First Useful Tests Were State-Transition Tests
&lt;/h2&gt;

&lt;p&gt;Traditional exchange integration tests answer questions such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Was the request authenticated?&lt;/li&gt;
&lt;li&gt;Did the exchange accept the order?&lt;/li&gt;
&lt;li&gt;Was an order ID returned?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Position Manager tests answer different questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does TP1 execute only once?&lt;/li&gt;
&lt;li&gt;Is the correct partial quantity returned?&lt;/li&gt;
&lt;li&gt;Does Break Even activate after TP1?&lt;/li&gt;
&lt;li&gt;Does a short position track the lowest price?&lt;/li&gt;
&lt;li&gt;Can trailing protection ever move backward?&lt;/li&gt;
&lt;li&gt;Does Stop Loss close only the remaining quantity?&lt;/li&gt;
&lt;li&gt;Does restart recovery preserve lifecycle flags?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A simplified test can replay a price sequence:&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;position&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;make_short_position&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="mf"&gt;64_000.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;qty&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.004&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tp1_price&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;62_000.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;manager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on_price_update&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="mf"&gt;61_900.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;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;tp1_hit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;close_qty&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="mf"&gt;0.002&lt;/span&gt;
&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;new_remaining_qty&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="mf"&gt;0.002&lt;/span&gt;
&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;new_stop_loss_price&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="mf"&gt;64_000.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important part is determinism.&lt;/p&gt;

&lt;p&gt;The same position and the same price should always produce the same transition.&lt;/p&gt;

&lt;p&gt;That property is far easier to preserve when the Position Manager does not perform network operations.&lt;/p&gt;




&lt;h2&gt;
  
  
  Looking Back
&lt;/h2&gt;

&lt;p&gt;The Position Manager was not part of the original design.&lt;/p&gt;

&lt;p&gt;It was extracted from repeated failures of the simpler model.&lt;/p&gt;

&lt;p&gt;Partial exits exposed the limits of independent variables.&lt;/p&gt;

&lt;p&gt;Break Even exposed hidden coupling between execution and risk state.&lt;/p&gt;

&lt;p&gt;Trailing Stop exposed the need to preserve market history.&lt;/p&gt;

&lt;p&gt;Restart recovery exposed the danger of in-memory ownership.&lt;/p&gt;

&lt;p&gt;Analytics exposed inconsistent quantity tracking.&lt;/p&gt;

&lt;p&gt;None of those failures required a more complicated trading strategy.&lt;/p&gt;

&lt;p&gt;They required a more explicit state model.&lt;/p&gt;

&lt;p&gt;Looking back, the biggest mistake was not writing poor code.&lt;/p&gt;

&lt;p&gt;It was believing that no Position Manager was necessary.&lt;/p&gt;

&lt;p&gt;That belief remained reasonable only while the platform managed one trivial lifecycle.&lt;/p&gt;

&lt;p&gt;The moment a position could evolve, persistence and ownership became architectural concerns.&lt;/p&gt;




&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;p&gt;A production-ready Position Manager is not a container for open orders.&lt;/p&gt;

&lt;p&gt;It is the system's memory of an active strategy.&lt;/p&gt;

&lt;p&gt;The most important lessons were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;an order and a position are different domain objects;&lt;/li&gt;
&lt;li&gt;partial exits require original and remaining quantity;&lt;/li&gt;
&lt;li&gt;price updates should produce explicit domain events;&lt;/li&gt;
&lt;li&gt;Break Even is a lifecycle transition, not only a number;&lt;/li&gt;
&lt;li&gt;Trailing Stop depends on historical state and monotonic invariants;&lt;/li&gt;
&lt;li&gt;predicted transitions must not be confused with confirmed execution;&lt;/li&gt;
&lt;li&gt;every behaviour-changing state update must be persistent;&lt;/li&gt;
&lt;li&gt;restart recovery must restore behaviour, not merely data;&lt;/li&gt;
&lt;li&gt;one component must own position mutation;&lt;/li&gt;
&lt;li&gt;a deterministic Position Manager is significantly easier to test.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The exchange can tell us that an order executed.&lt;/p&gt;

&lt;p&gt;It cannot tell us what that execution means to the strategy.&lt;/p&gt;

&lt;p&gt;That responsibility belongs to the platform.&lt;/p&gt;




&lt;h2&gt;
  
  
  Engineering Principle
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A position is not an order. It's the memory of your trading strategy.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




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

&lt;p&gt;The Position Manager described in this article is just one component of a larger algorithmic trading platform that I've been building over the past several months.&lt;/p&gt;

&lt;p&gt;If you're curious about the overall architecture and how these components fit together, you can learn more about the project here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://py-dev.top/application-software/bybit-signal-trading-bot"&gt;https://py-dev.top/application-software/bybit-signal-trading-bot&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Throughout this series, I'm documenting the engineering decisions that shaped the platform rather than publishing its complete source code.&lt;/p&gt;

&lt;p&gt;The next chapter continues this architectural evolution by introducing the &lt;strong&gt;Risk Management Engine&lt;/strong&gt;—the component responsible for transforming position state into real-time risk decisions.&lt;/p&gt;

&lt;p&gt;We'll explore how a production-ready Risk Management Engine coordinates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stop Loss&lt;/li&gt;
&lt;li&gt;Position limits&lt;/li&gt;
&lt;li&gt;Partial Take Profit&lt;/li&gt;
&lt;li&gt;Break Even&lt;/li&gt;
&lt;li&gt;Trailing Stop&lt;/li&gt;
&lt;li&gt;Reverse Signals&lt;/li&gt;
&lt;li&gt;Leverage&lt;/li&gt;
&lt;li&gt;Emergency Close&lt;/li&gt;
&lt;li&gt;Spot and Futures risk management&lt;/li&gt;
&lt;li&gt;Dry Run, Testnet, and Mainnet safeguards&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 &lt;strong&gt;Next article:&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Building a Production-Ready Risk Management Engine for Algorithmic Trading&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/pydevtop/building-a-production-ready-risk-management-engine-for-algorithmic-trading-1jh8"&gt;https://dev.to/pydevtop/building-a-production-ready-risk-management-engine-for-algorithmic-trading-1jh8&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>bybit</category>
      <category>tradingview</category>
      <category>websockets</category>
    </item>
    <item>
      <title>Designing a Modular Trade Execution Engine in Python</title>
      <dc:creator>PyDev</dc:creator>
      <pubDate>Wed, 15 Jul 2026 12:54:42 +0000</pubDate>
      <link>https://dev.to/pydevtop/designing-a-modular-trade-execution-engine-in-python-5ebh</link>
      <guid>https://dev.to/pydevtop/designing-a-modular-trade-execution-engine-in-python-5ebh</guid>
      <description>&lt;p&gt;&lt;em&gt;&lt;strong&gt;Series:&lt;/strong&gt; Engineering an Algorithmic Trading Platform in Python&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Part:&lt;/strong&gt; 2 of 18&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;About this series&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This series documents the engineering decisions behind building a production-ready algorithmic trading platform in Python. The focus is not on trading strategies or profit claims. It is on software architecture, trade execution, state management, real-time data processing, and system reliability.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In the first article, I described how a simple TradingView webhook project gradually evolved into a modular algorithmic trading platform.&lt;/p&gt;

&lt;p&gt;The first version was straightforward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TradingView
      │
      ▼
FastAPI Webhook
      │
      ▼
Bybit API
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A signal arrived, Python parsed the payload, and the application submitted an order.&lt;/p&gt;

&lt;p&gt;That flow was enough to prove that the integration worked.&lt;/p&gt;

&lt;p&gt;It was not enough to build reliable trading software.&lt;/p&gt;

&lt;p&gt;As soon as the platform needed to support Spot orders, Futures short positions, reverse signals, disabled pairs, position limits, restart recovery, partial exits, and multiple signal sources, one component became unavoidable:&lt;/p&gt;

&lt;h2&gt;
  
  
  the Trade Execution Engine
&lt;/h2&gt;

&lt;p&gt;The Trade Execution Engine is the layer that converts external intent into controlled trading actions.&lt;/p&gt;

&lt;p&gt;It receives normalized signals from TradingView or a local Python signal engine, checks the current system state, validates the request against configuration, decides whether execution is allowed, and routes the action to the correct exchange method.&lt;/p&gt;

&lt;p&gt;That sounds simple when written as one sentence.&lt;/p&gt;

&lt;p&gt;In practice, this layer becomes the boundary between untrusted external input and real exchange activity.&lt;/p&gt;

&lt;p&gt;A mistake here does not produce a broken page or a failed background job.&lt;/p&gt;

&lt;p&gt;It can open the wrong market, use the wrong position side, execute the same signal twice, or create a position that the rest of the platform cannot manage correctly.&lt;/p&gt;

&lt;p&gt;This article explores the architecture of a modular Python Trade Execution Engine, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;signal normalization;&lt;/li&gt;
&lt;li&gt;action routing;&lt;/li&gt;
&lt;li&gt;pair configuration;&lt;/li&gt;
&lt;li&gt;Spot and Futures separation;&lt;/li&gt;
&lt;li&gt;state-aware decisions;&lt;/li&gt;
&lt;li&gt;reverse signals;&lt;/li&gt;
&lt;li&gt;duplicate protection;&lt;/li&gt;
&lt;li&gt;order execution flow;&lt;/li&gt;
&lt;li&gt;error boundaries;&lt;/li&gt;
&lt;li&gt;persistent position state;&lt;/li&gt;
&lt;li&gt;Dry Run, Testnet, and Mainnet modes;&lt;/li&gt;
&lt;li&gt;trade-offs discovered during development.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The examples are based on a real Python algorithmic trading platform built with FastAPI, TradingView webhooks, the Bybit API, WebSockets, SQLite analytics, and Telegram control.&lt;/p&gt;




&lt;h2&gt;
  
  
  What the Trade Execution Engine Actually Does
&lt;/h2&gt;

&lt;p&gt;The name can be misleading.&lt;/p&gt;

&lt;p&gt;A Trade Execution Engine should not be one large function that contains the complete trading platform.&lt;/p&gt;

&lt;p&gt;It should not:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;calculate EMA, RSI, or MACD;&lt;/li&gt;
&lt;li&gt;receive raw HTTP requests;&lt;/li&gt;
&lt;li&gt;maintain Telegram menus;&lt;/li&gt;
&lt;li&gt;calculate monthly trading analytics;&lt;/li&gt;
&lt;li&gt;subscribe directly to every WebSocket stream;&lt;/li&gt;
&lt;li&gt;format user notifications;&lt;/li&gt;
&lt;li&gt;contain exchange credentials;&lt;/li&gt;
&lt;li&gt;store unrelated application settings.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Its responsibility is narrower:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Accept a validated trading signal, evaluate whether the action is allowed, dispatch the correct execution request, and synchronize the resulting position state.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A simplified flow 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;Normalized Signal
       │
       ▼
Trade Execution Engine
       │
       ├── Validate symbol
       ├── Validate action
       ├── Load pair configuration
       ├── Check enabled state
       ├── Check execution mode
       ├── Inspect open positions
       ├── Apply signal rules
       └── Dispatch execution
                 │
                 ▼
        Exchange Client
                 │
                 ▼
        Position Manager
                 │
                 ▼
          Persistent State
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This boundary is important because signals and orders are not the same thing.&lt;/p&gt;

&lt;p&gt;A signal expresses intent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"symbol"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"BTCUSDT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SELL_FUTURES"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;62480.5&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An exchange order requires operational details:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"category"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"linear"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"symbol"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"BTCUSDT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"side"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Sell"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"orderType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Market"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"qty"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0.001"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Trade Execution Engine is responsible for translating one into the other without allowing infrastructure details to leak into signal sources.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Engineering decision&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;TradingView should describe what the strategy wants to do. It should not need to know how the exchange API expects that action to be executed.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Why the Webhook Should Not Contain Trading Logic
&lt;/h2&gt;

&lt;p&gt;It is tempting to process everything directly inside the FastAPI route.&lt;/p&gt;

&lt;p&gt;A minimal example often looks like this:&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="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;def&lt;/span&gt; &lt;span class="nf"&gt;webhook&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="nb"&gt;dict&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;payload&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="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;bybit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;place_spot_buy&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;payload&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;symbol&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="n"&gt;qty&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;qty&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="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;ok&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works until the application needs any real rules.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the pair may be disabled;&lt;/li&gt;
&lt;li&gt;the signal may use an unsupported action;&lt;/li&gt;
&lt;li&gt;another position may already be open;&lt;/li&gt;
&lt;li&gt;the same webhook may arrive twice;&lt;/li&gt;
&lt;li&gt;the platform may be in Dry Run mode;&lt;/li&gt;
&lt;li&gt;Spot buying may be disabled while Futures trading remains enabled;&lt;/li&gt;
&lt;li&gt;the symbol may not exist in the local order configuration;&lt;/li&gt;
&lt;li&gt;a reverse signal may require closing one position before opening another;&lt;/li&gt;
&lt;li&gt;the exchange may accept the order but return incomplete execution data;&lt;/li&gt;
&lt;li&gt;position state may fail to persist after the order succeeds.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If all of those checks live inside the webhook endpoint, the API layer becomes tightly coupled to trading behaviour.&lt;/p&gt;

&lt;p&gt;That creates several problems.&lt;/p&gt;

&lt;h3&gt;
  
  
  It becomes difficult to test
&lt;/h3&gt;

&lt;p&gt;Testing an HTTP route should not require a live exchange client.&lt;/p&gt;

&lt;h3&gt;
  
  
  It becomes difficult to reuse
&lt;/h3&gt;

&lt;p&gt;A local Python signal engine cannot reuse webhook-only logic cleanly.&lt;/p&gt;

&lt;h3&gt;
  
  
  It becomes difficult to control
&lt;/h3&gt;

&lt;p&gt;Telegram actions, TradingView webhooks, and internal signals start following different execution paths.&lt;/p&gt;

&lt;h3&gt;
  
  
  It becomes difficult to recover
&lt;/h3&gt;

&lt;p&gt;If order execution and persistence fail at different stages, there is no single component responsible for resolving the inconsistency.&lt;/p&gt;

&lt;p&gt;A cleaner webhook endpoint only validates and forwards the request.&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;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;HTTPException&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="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;def&lt;/span&gt; &lt;span class="nf"&gt;webhook&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="nb"&gt;dict&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;payload&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;secret&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;settings&lt;/span&gt;&lt;span class="p"&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="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;Invalid 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;signal&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;symbol&lt;/span&gt;&lt;span class="sh"&gt;"&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;payload&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;symbol&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;upper&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="nf"&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="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;action&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;upper&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;price&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;float&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="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;price&lt;/span&gt;&lt;span class="sh"&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;source&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;tradingview&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;trade_engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;process_signal&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The HTTP layer remains stateless.&lt;/p&gt;

&lt;p&gt;The Trade Execution Engine owns the trading decision.&lt;/p&gt;




&lt;h2&gt;
  
  
  Designing a Stable Signal Contract
&lt;/h2&gt;

&lt;p&gt;Before the engine can route anything, it needs a stable signal format.&lt;/p&gt;

&lt;p&gt;Different signal sources often produce different payloads.&lt;/p&gt;

&lt;p&gt;TradingView may send:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"secret"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"test123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"strategy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ema-cross"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"exchange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"BYBIT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"symbol"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"BTCUSDT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"interval"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"15"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"62480.5"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"time"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-07-15T10:30:00Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SELL_FUTURES"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A local signal engine may produce:&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="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;symbol&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;BTCUSDT&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;action&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_FUTURES&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;price&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;62480.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;strategy&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;ema_rsi_macd&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;interval&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;15&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;source&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;pydev_signal_engine&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;A Telegram emergency action may not have a market price at all:&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="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;symbol&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;BTCUSDT&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;action&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_ALL&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;source&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;telegram&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;If every source uses a different internal representation, the engine becomes full of special cases.&lt;/p&gt;

&lt;p&gt;The solution is signal normalization.&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;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;

&lt;span class="n"&gt;SUPPORTED_ACTIONS&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;BUY_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;SELL_FUTURES&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_ALL&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;normalize_signal&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="nb"&gt;dict&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;Any&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;dict&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;Any&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="nf"&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="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;symbol&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;strip&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="n"&gt;action&lt;/span&gt; &lt;span class="o"&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;payload&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;action&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;strip&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="n"&gt;price_raw&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="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;price&lt;/span&gt;&lt;span class="sh"&gt;"&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="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;price_raw&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_raw&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&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="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;symbol&lt;/span&gt;&lt;span class="sh"&gt;"&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="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="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;price&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;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;strategy&lt;/span&gt;&lt;span class="sh"&gt;"&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="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;strategy&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;interval&lt;/span&gt;&lt;span class="sh"&gt;"&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;payload&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;interval&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;source&lt;/span&gt;&lt;span class="sh"&gt;"&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="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;source&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;unknown&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;payload&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;signal_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;received_at&lt;/span&gt;&lt;span class="sh"&gt;"&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="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;received_at&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;Every source is converted into the same contract before reaching the Trade Execution Engine.&lt;/p&gt;

&lt;p&gt;That gives the engine a predictable input.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key takeaway&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A stable internal signal contract is more important than the original payload format. External integrations can change without forcing the core engine to change with them.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Explicit Trading Actions Are Better Than Ambiguous BUY and SELL
&lt;/h2&gt;

&lt;p&gt;Generic &lt;code&gt;BUY&lt;/code&gt; and &lt;code&gt;SELL&lt;/code&gt; actions are often insufficient when one platform manages both Spot and Futures.&lt;/p&gt;

&lt;p&gt;Consider the word &lt;code&gt;SELL&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It may mean:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;sell an existing Spot balance;&lt;/li&gt;
&lt;li&gt;open a Futures short position;&lt;/li&gt;
&lt;li&gt;close a Futures long position;&lt;/li&gt;
&lt;li&gt;reduce a short position;&lt;/li&gt;
&lt;li&gt;close the entire position;&lt;/li&gt;
&lt;li&gt;execute a partial take profit.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those are completely different operations.&lt;/p&gt;

&lt;p&gt;For this reason, the platform uses explicit actions:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;The same principle can be extended later with actions such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELL_SPOT
BUY_FUTURES
CLOSE_SPOT
CLOSE_FUTURES
REDUCE_POSITION
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exact action set depends on platform scope.&lt;/p&gt;

&lt;p&gt;The important part is that the command should describe intent clearly enough that the engine does not need to guess.&lt;/p&gt;

&lt;p&gt;A basic validation step looks like this:&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;validate_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;action&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;tuple&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;bool&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="p"&gt;]:&lt;/span&gt;
    &lt;span class="n"&gt;allowed&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;BUY_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;SELL_FUTURES&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_ALL&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="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;action&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&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="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;unsupported_action_&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="si"&gt;}&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="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes logs and analytics easier to interpret.&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;is much more useful than silently executing the wrong operation.&lt;/p&gt;




&lt;h2&gt;
  
  
  Loading Pair Configuration
&lt;/h2&gt;

&lt;p&gt;The Trade Execution Engine should not hardcode trading amounts, leverage, take-profit settings, or pair permissions.&lt;/p&gt;

&lt;p&gt;Those values belong in pair configuration.&lt;/p&gt;

&lt;p&gt;A simplified &lt;code&gt;orders.ini&lt;/code&gt; section may look like this:&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;[BTCUSDT]&lt;/span&gt;
&lt;span class="py"&gt;bybit_pair&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;BTCUSDT&lt;/span&gt;
&lt;span class="py"&gt;enabled&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;True&lt;/span&gt;
&lt;span class="py"&gt;spot_amount&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;100.0&lt;/span&gt;
&lt;span class="py"&gt;futures_qty&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;0.001&lt;/span&gt;
&lt;span class="py"&gt;take_profit_1_percent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;5.0&lt;/span&gt;
&lt;span class="py"&gt;take_profit_2_percent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;10.0&lt;/span&gt;
&lt;span class="py"&gt;stop_loss_percent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;5.0&lt;/span&gt;
&lt;span class="py"&gt;close_percent_on_tp1&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;50.0&lt;/span&gt;
&lt;span class="py"&gt;close_percent_on_tp2&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;50.0&lt;/span&gt;
&lt;span class="py"&gt;move_sl_to_breakeven&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;True&lt;/span&gt;
&lt;span class="py"&gt;trailing_percent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;3.0&lt;/span&gt;
&lt;span class="py"&gt;allow_spot_buy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;True&lt;/span&gt;
&lt;span class="py"&gt;allow_futures_short&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;True&lt;/span&gt;
&lt;span class="py"&gt;max_open_positions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;1&lt;/span&gt;
&lt;span class="py"&gt;leverage&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;10&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The engine receives only the symbol.&lt;/p&gt;

&lt;p&gt;It loads everything else from the platform configuration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_order_by_symbol&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="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&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;for&lt;/span&gt; &lt;span class="n"&gt;section_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="ow"&gt;in&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;orders&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;order&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;bybit_pair&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;upper&lt;/span&gt;&lt;span class="p"&gt;()&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;order&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;This creates a clean separation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Signal Source
    │
    └── symbol + action

Trade Execution Engine
    │
    └── configuration lookup

Execution Layer
    │
    └── exchange-specific parameters
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The signal source does not control leverage or quantity unless the system explicitly allows it.&lt;/p&gt;

&lt;p&gt;That is a safer default for automated trading infrastructure.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rejecting Unknown and Disabled Pairs
&lt;/h2&gt;

&lt;p&gt;A production engine should fail closed.&lt;/p&gt;

&lt;p&gt;If a symbol is not configured, it should not improvise.&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;order_config&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;get_order_by_symbol&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;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;order_config&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;[REJECT] Pair not found in orders.ini: &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;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;ok&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;pair_not_found&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;symbol&lt;/span&gt;&lt;span class="sh"&gt;"&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same applies to disabled pairs.&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;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;order_config&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;enabled&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="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;[REJECT] Pair disabled: &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;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;ok&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;pair_disabled&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;symbol&lt;/span&gt;&lt;span class="sh"&gt;"&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This may look like a small detail, but it prevents an external strategy from activating symbols that the platform operator did not approve.&lt;/p&gt;

&lt;p&gt;The configuration is the authority.&lt;/p&gt;

&lt;p&gt;The signal is only a request.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Common pitfall&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Treating every valid-looking webhook as permission to trade. A syntactically valid signal can still violate local configuration and risk rules.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Separating Spot and Futures Execution
&lt;/h2&gt;

&lt;p&gt;Spot and Futures orders may look similar at first.&lt;/p&gt;

&lt;p&gt;Both submit market orders.&lt;/p&gt;

&lt;p&gt;Both use a symbol and quantity.&lt;/p&gt;

&lt;p&gt;Both return an exchange order ID.&lt;/p&gt;

&lt;p&gt;But their semantics are different.&lt;/p&gt;

&lt;h3&gt;
  
  
  Spot BUY
&lt;/h3&gt;

&lt;p&gt;A Spot buy typically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;spends quote currency;&lt;/li&gt;
&lt;li&gt;acquires base currency;&lt;/li&gt;
&lt;li&gt;does not use leverage;&lt;/li&gt;
&lt;li&gt;cannot normally create a negative asset balance;&lt;/li&gt;
&lt;li&gt;may use quote amount or base quantity depending on API parameters.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A Bybit Spot market buy can be represented like this:&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;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;place_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;category&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;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="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="n"&gt;orderType&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;marketUnit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;BaseCoin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;qty&lt;/span&gt;&lt;span class="o"&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;qty&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Futures short
&lt;/h3&gt;

&lt;p&gt;A Futures short typically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;opens a derivative position;&lt;/li&gt;
&lt;li&gt;uses leverage;&lt;/li&gt;
&lt;li&gt;has liquidation risk;&lt;/li&gt;
&lt;li&gt;can incur funding;&lt;/li&gt;
&lt;li&gt;requires reduce-only logic when closing;&lt;/li&gt;
&lt;li&gt;must distinguish between opening and closing sides.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A Bybit linear Futures short can be represented like this:&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;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;place_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;linear&lt;/span&gt;&lt;span class="sh"&gt;"&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="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;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;orderType&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;qty&lt;/span&gt;&lt;span class="o"&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;qty&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;Using one generic &lt;code&gt;place_order()&lt;/code&gt; call for both markets can hide important differences.&lt;/p&gt;

&lt;p&gt;The engine should route them explicitly.&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;if&lt;/span&gt; &lt;span class="n"&gt;action&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_SPOT&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="nf"&gt;_execute_spot_buy&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="n"&gt;order_config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;order_config&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;signal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;action&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_FUTURES&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="nf"&gt;_execute_futures_short&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="n"&gt;order_config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;order_config&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;signal&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;This keeps market-specific rules isolated.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Practical &lt;code&gt;process_signal()&lt;/code&gt; Structure
&lt;/h2&gt;

&lt;p&gt;The public engine method should remain readable.&lt;/p&gt;

&lt;p&gt;It should describe the execution pipeline without containing every implementation detail.&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;process_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;signal&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="o"&gt;-&amp;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;symbol&lt;/span&gt; &lt;span class="o"&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;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;symbol&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;upper&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&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;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;action&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;upper&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;[TRADE ENGINE] signal=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;signal&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;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;symbol&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CLOSE_ALL&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="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="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;empty_symbol&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="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="ow"&gt;not&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_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;SELL_FUTURES&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_ALL&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="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;ok&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;unsupported_action_&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;action&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="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CLOSE_ALL&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="nf"&gt;close_all_positions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="o"&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;source&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;unknown&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;order_config&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;get_order_by_symbol&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;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;order_config&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;ok&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;pair_not_found&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;symbol&lt;/span&gt;&lt;span class="sh"&gt;"&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="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;order_config&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;enabled&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="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;ok&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;pair_disabled&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;symbol&lt;/span&gt;&lt;span class="sh"&gt;"&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="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;action&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_SPOT&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="nf"&gt;_handle_spot_buy&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;signal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;order_config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;order_config&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_handle_futures_short&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;signal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;order_config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;order_config&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;This method acts as an orchestration layer.&lt;/p&gt;

&lt;p&gt;It does not need to know every exchange parameter or every persistence field.&lt;/p&gt;

&lt;p&gt;Those details belong in smaller components.&lt;/p&gt;




&lt;h2&gt;
  
  
  State Awareness Before Execution
&lt;/h2&gt;

&lt;p&gt;A trading action should never be evaluated in isolation.&lt;/p&gt;

&lt;p&gt;The engine needs to inspect current platform state.&lt;/p&gt;

&lt;p&gt;Suppose &lt;code&gt;SELL_FUTURES&lt;/code&gt; arrives for &lt;code&gt;BTCUSDT&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The correct response depends on what already exists.&lt;/p&gt;

&lt;h3&gt;
  
  
  No open position
&lt;/h3&gt;

&lt;p&gt;The engine may open a new short.&lt;/p&gt;

&lt;h3&gt;
  
  
  Existing short position
&lt;/h3&gt;

&lt;p&gt;The engine may reject the duplicate, ignore it, or add to the position depending on platform policy.&lt;/p&gt;

&lt;h3&gt;
  
  
  Existing Spot position
&lt;/h3&gt;

&lt;p&gt;The engine may allow both positions, or it may treat the signal as a reverse action.&lt;/p&gt;

&lt;h3&gt;
  
  
  Position already closing
&lt;/h3&gt;

&lt;p&gt;The engine should avoid starting another conflicting transition.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pair disabled after the signal was generated
&lt;/h3&gt;

&lt;p&gt;The engine should reject the action.&lt;/p&gt;

&lt;p&gt;This is why persistent position state is not just an analytics feature.&lt;/p&gt;

&lt;p&gt;It is part of execution safety.&lt;/p&gt;

&lt;p&gt;A simplified check may look like this:&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;position&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_open_position&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;if&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;current_side&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;position&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;side&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;current_market&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;position&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;market&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;action&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_FUTURES&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;current_market&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;futures&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;current_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;short&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="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="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;short_position_already_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;symbol&lt;/span&gt;&lt;span class="sh"&gt;"&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exact policy may differ between strategies.&lt;/p&gt;

&lt;p&gt;What matters is that it is explicit.&lt;/p&gt;




&lt;h2&gt;
  
  
  Reverse Signals Need a Defined State Transition
&lt;/h2&gt;

&lt;p&gt;Reverse signals are more complicated than they appear.&lt;/p&gt;

&lt;p&gt;A naive implementation might do this:&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;if&lt;/span&gt; &lt;span class="n"&gt;signal&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="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;close_buy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;open_short&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But several questions remain.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Was the first position fully closed?&lt;/li&gt;
&lt;li&gt;Was it partially filled?&lt;/li&gt;
&lt;li&gt;Did the close request fail?&lt;/li&gt;
&lt;li&gt;Is the remaining quantity zero?&lt;/li&gt;
&lt;li&gt;Should the new position open immediately?&lt;/li&gt;
&lt;li&gt;Should the engine wait for exchange confirmation?&lt;/li&gt;
&lt;li&gt;What happens if a second reverse signal arrives during the transition?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A safer model treats reversal as two separate state transitions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OPEN SPOT POSITION
        │
        ▼
REVERSE SIGNAL RECEIVED
        │
        ▼
CLOSE CURRENT POSITION
        │
        ▼
VERIFY CLOSED STATE
        │
        ▼
OPEN FUTURES SHORT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second action should not occur until the first one is confirmed.&lt;/p&gt;

&lt;p&gt;A high-level implementation may look like this:&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;handle_reverse_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;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;target_action&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;signal&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="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;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;close_result&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;close_position&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="n"&gt;reason&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reverse_signal&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="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;close_result&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;ok&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="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="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;reverse_close_failed&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;details&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;close_result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;target_action&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_FUTURES&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="nf"&gt;_handle_futures_short&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;signal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;order_config&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;get_order_by_symbol&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="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;ok&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;unsupported_reverse_target&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;In a more advanced implementation, the engine may wait for execution confirmation before opening the opposite position.&lt;/p&gt;

&lt;p&gt;That avoids overlapping exposure.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Engineering note&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A reverse signal is not one order. It is a coordinated workflow with at least two exchange operations and one persistent state transition.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Duplicate Signal Protection
&lt;/h2&gt;

&lt;p&gt;Duplicate signals are an important risk in webhook-driven trading systems.&lt;/p&gt;

&lt;p&gt;Duplicates can happen because of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;TradingView alert configuration;&lt;/li&gt;
&lt;li&gt;webhook retries;&lt;/li&gt;
&lt;li&gt;network timeouts;&lt;/li&gt;
&lt;li&gt;manual resubmission;&lt;/li&gt;
&lt;li&gt;strategy bugs;&lt;/li&gt;
&lt;li&gt;multiple signal sources producing the same action;&lt;/li&gt;
&lt;li&gt;application restart during request processing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are several ways to handle this.&lt;/p&gt;

&lt;h3&gt;
  
  
  Signal ID
&lt;/h3&gt;

&lt;p&gt;The best option is a unique identifier generated by the source.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"signal_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ema-cross-BTCUSDT-15m-20260715T103000Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"symbol"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"BTCUSDT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SELL_FUTURES"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The engine stores processed IDs.&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;if&lt;/span&gt; &lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;signal_exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;signal_id&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;ok&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;duplicate_signal&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_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Time-window fingerprint
&lt;/h3&gt;

&lt;p&gt;When the source cannot provide a unique ID, the platform can derive a fingerprint.&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;hashlib&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;build_signal_fingerprint&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="nb"&gt;dict&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;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;symbol&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;symbol&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;action&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;action&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;strategy&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;strategy&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;interval&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;interval&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;time&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;time&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;encoded&lt;/span&gt; &lt;span class="o"&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="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;sort_keys&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;separators&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;,&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;:&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="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&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;hashlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sha256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;encoded&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;hexdigest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The fingerprint can be stored with a short expiration window.&lt;/p&gt;

&lt;h3&gt;
  
  
  State-based rejection
&lt;/h3&gt;

&lt;p&gt;Even without a signal ID, current position state may reject repeated actions.&lt;/p&gt;

&lt;p&gt;For example, if a short is already open, another identical &lt;code&gt;SELL_FUTURES&lt;/code&gt; signal can be ignored.&lt;/p&gt;

&lt;p&gt;State-based protection is useful, but it is not a complete substitute for idempotency.&lt;/p&gt;

&lt;p&gt;Two duplicate requests can arrive close enough together that both inspect the state before either one persists the new position.&lt;/p&gt;

&lt;p&gt;That creates a race condition.&lt;/p&gt;




&lt;h2&gt;
  
  
  Idempotency and Race Conditions
&lt;/h2&gt;

&lt;p&gt;This is where duplicate protection becomes a concurrency problem.&lt;/p&gt;

&lt;p&gt;Consider two identical webhook requests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Request A                 Request B
    │                         │
    ▼                         ▼
Check position           Check position
No position              No position
    │                         │
    ▼                         ▼
Place short              Place short
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both requests passed validation.&lt;/p&gt;

&lt;p&gt;Both saw the same state.&lt;/p&gt;

&lt;p&gt;Both submitted an order.&lt;/p&gt;

&lt;p&gt;The result is double exposure.&lt;/p&gt;

&lt;p&gt;A reliable implementation needs an atomic boundary around the critical section.&lt;/p&gt;

&lt;p&gt;For a single-process platform, a per-symbol lock may be enough.&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;collections&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;defaultdict&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;threading&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Lock&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SymbolLockRegistry&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="o"&gt;-&amp;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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_locks&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="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Lock&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;defaultdict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Lock&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&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="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Lock&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;_locks&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;Then:&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;lock&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;symbol_locks&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;symbol&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;lock&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="n"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_open_position&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;if&lt;/span&gt; &lt;span class="n"&gt;position&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;ok&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;position_already_open&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="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="nf"&gt;_execute_futures_short&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="n"&gt;order_config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;order_config&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;signal&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;For a distributed architecture with multiple workers, an in-memory lock is not enough.&lt;/p&gt;

&lt;p&gt;The platform would need a database transaction, distributed lock, queue partitioning, or another atomic coordination mechanism.&lt;/p&gt;

&lt;p&gt;The current platform is self-hosted and single-user, so the simpler approach is appropriate.&lt;/p&gt;

&lt;p&gt;That may change if the architecture becomes multi-worker or multi-user.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Trade-off&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The simplest concurrency model that matches the deployment environment is usually better than adding distributed infrastructure prematurely.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Execution Modes: Dry Run, Testnet, and Mainnet
&lt;/h2&gt;

&lt;p&gt;A Trade Execution Engine should not assume every validated signal must reach a live exchange account.&lt;/p&gt;

&lt;p&gt;The platform supports three operational modes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dry Run
&lt;/h3&gt;

&lt;p&gt;No exchange order is sent.&lt;/p&gt;

&lt;p&gt;The engine simulates execution and creates internal position state.&lt;/p&gt;

&lt;p&gt;This is useful for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;testing signal routing;&lt;/li&gt;
&lt;li&gt;validating risk logic;&lt;/li&gt;
&lt;li&gt;testing Telegram workflows;&lt;/li&gt;
&lt;li&gt;testing analytics;&lt;/li&gt;
&lt;li&gt;reproducing state transitions safely.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Testnet
&lt;/h3&gt;

&lt;p&gt;The engine submits real API requests to the exchange test environment.&lt;/p&gt;

&lt;p&gt;This validates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API authentication;&lt;/li&gt;
&lt;li&gt;order parameters;&lt;/li&gt;
&lt;li&gt;exchange responses;&lt;/li&gt;
&lt;li&gt;execution IDs;&lt;/li&gt;
&lt;li&gt;WebSocket subscriptions;&lt;/li&gt;
&lt;li&gt;position lifecycle logic.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Mainnet
&lt;/h3&gt;

&lt;p&gt;The engine submits live orders.&lt;/p&gt;

&lt;p&gt;This mode should require explicit configuration and clear UI status.&lt;/p&gt;

&lt;p&gt;A clean execution abstraction may look like this:&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;execute_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;request&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="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&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;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;dry_run&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;dry_run_executor&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;request&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;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;testnet&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;testnet_client&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;request&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;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;mainnet&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;mainnet_client&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;request&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;ok&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;unsupported_mode_&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;mode&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Trade Execution Engine should not duplicate all logic three times.&lt;/p&gt;

&lt;p&gt;Only the final execution adapter should change.&lt;/p&gt;




&lt;h2&gt;
  
  
  Order Execution and Position Persistence
&lt;/h2&gt;

&lt;p&gt;Submitting the order is not the final step.&lt;/p&gt;

&lt;p&gt;The platform must also create persistent position state.&lt;/p&gt;

&lt;p&gt;A simplified Futures position record may contain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"symbol"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"BTCUSDT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"market"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"futures"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"side"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"short"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"entry_price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;62480.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"qty"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.001&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"remaining_qty"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.001&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tp1_price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;59356.475&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tp2_price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;56232.45&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"stop_loss_price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;65604.525&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"breakeven_price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;62480.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tp1_hit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tp2_hit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"trailing_active"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"leverage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"strategy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ema_rsi_macd"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"interval"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"15"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"signal_source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tradingview"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"open_order_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"123456789"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"open"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"testnet"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"created_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-07-15T10:30:00Z"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This state is later used by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the Position Manager;&lt;/li&gt;
&lt;li&gt;the WebSocket price handler;&lt;/li&gt;
&lt;li&gt;take-profit logic;&lt;/li&gt;
&lt;li&gt;stop-loss logic;&lt;/li&gt;
&lt;li&gt;restart recovery;&lt;/li&gt;
&lt;li&gt;Telegram reports;&lt;/li&gt;
&lt;li&gt;analytics after closure.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The execution flow becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Signal accepted
      │
      ▼
Order request built
      │
      ▼
Exchange order submitted
      │
      ▼
Execution response validated
      │
      ▼
Position state created
      │
      ▼
WebSocket subscription activated
      │
      ▼
Telegram status updated
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A failure at any stage must be visible.&lt;/p&gt;

&lt;p&gt;Silently accepting an exchange order without storing the position is dangerous.&lt;/p&gt;

&lt;p&gt;The exchange now has exposure, but the local platform may not know it exists.&lt;/p&gt;




&lt;h2&gt;
  
  
  Handling Exchange Errors
&lt;/h2&gt;

&lt;p&gt;Exchange APIs fail in many ways.&lt;/p&gt;

&lt;p&gt;Examples include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;invalid quantity precision;&lt;/li&gt;
&lt;li&gt;insufficient balance;&lt;/li&gt;
&lt;li&gt;unsupported symbol;&lt;/li&gt;
&lt;li&gt;authentication errors;&lt;/li&gt;
&lt;li&gt;rate limits;&lt;/li&gt;
&lt;li&gt;temporary network failures;&lt;/li&gt;
&lt;li&gt;rejected leverage settings;&lt;/li&gt;
&lt;li&gt;market restrictions;&lt;/li&gt;
&lt;li&gt;malformed API responses.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The engine should not convert every exception into a generic failure.&lt;/p&gt;

&lt;p&gt;Structured errors make debugging easier.&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;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;result&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;bybit_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open_futures_short&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="n"&gt;qty&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;qty&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;TimeoutError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;exc&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;ok&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;exchange_timeout&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;symbol&lt;/span&gt;&lt;span class="sh"&gt;"&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;"&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;exc&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;ValueError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;exc&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;ok&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;invalid_order_parameters&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;symbol&lt;/span&gt;&lt;span class="sh"&gt;"&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;"&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;exc&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;exc&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;ok&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;exchange_execution_error&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;symbol&lt;/span&gt;&lt;span class="sh"&gt;"&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;"&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;exc&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 external API response should remain safe.&lt;/p&gt;

&lt;p&gt;Sensitive credentials, signatures, and internal exception details should never be returned to TradingView.&lt;/p&gt;

&lt;p&gt;Detailed errors can still be written to logs or sent to the operator through Telegram.&lt;/p&gt;




&lt;h2&gt;
  
  
  Do Not Trust the Requested Price as the Entry Price
&lt;/h2&gt;

&lt;p&gt;TradingView may send a price with the signal.&lt;/p&gt;

&lt;p&gt;That price is useful for context.&lt;/p&gt;

&lt;p&gt;It is not necessarily the actual fill price.&lt;/p&gt;

&lt;p&gt;Between signal generation and exchange execution:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the market can move;&lt;/li&gt;
&lt;li&gt;the webhook can be delayed;&lt;/li&gt;
&lt;li&gt;the order can slip;&lt;/li&gt;
&lt;li&gt;the order can fill at multiple prices.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Therefore, the final position should use exchange execution data whenever possible.&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;signal_price&lt;/span&gt; &lt;span class="o"&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;price&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;execution_price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&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;avg_price&lt;/span&gt;&lt;span class="sh"&gt;"&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;execution_price&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;signal_price&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The fallback is useful in Dry Run mode or when the exchange response does not immediately provide an average fill price.&lt;/p&gt;

&lt;p&gt;For live analytics, exchange execution data should remain the source of truth.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Lesson learned&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Strategy price, requested price, order price, and execution price are not always the same value.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Keeping the Engine Independent from Analytics
&lt;/h2&gt;

&lt;p&gt;It is tempting to calculate fees, ROI, and PnL directly after every order.&lt;/p&gt;

&lt;p&gt;That creates tight coupling.&lt;/p&gt;

&lt;p&gt;The Trade Execution Engine should record execution facts.&lt;/p&gt;

&lt;p&gt;The Analytics Engine should calculate historical metrics.&lt;/p&gt;

&lt;p&gt;For example, the execution layer may store:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"symbol"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"BTCUSDT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"order_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"123456789"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"side"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Sell"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"market"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"futures"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"qty"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.001&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;62480.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"fee"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.0312&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"timestamp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-07-15T10:30:02Z"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Later, analytics can calculate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;gross PnL;&lt;/li&gt;
&lt;li&gt;net PnL;&lt;/li&gt;
&lt;li&gt;ROI;&lt;/li&gt;
&lt;li&gt;trading fees;&lt;/li&gt;
&lt;li&gt;funding;&lt;/li&gt;
&lt;li&gt;win rate;&lt;/li&gt;
&lt;li&gt;average holding time;&lt;/li&gt;
&lt;li&gt;monthly profit;&lt;/li&gt;
&lt;li&gt;pair statistics.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This separation prevents reporting changes from affecting order execution.&lt;/p&gt;




&lt;h2&gt;
  
  
  Keeping Telegram Outside the Core Engine
&lt;/h2&gt;

&lt;p&gt;Telegram is an operational control panel.&lt;/p&gt;

&lt;p&gt;It can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;show open positions;&lt;/li&gt;
&lt;li&gt;enable or disable pairs;&lt;/li&gt;
&lt;li&gt;display analytics;&lt;/li&gt;
&lt;li&gt;change signal engine settings;&lt;/li&gt;
&lt;li&gt;trigger emergency close;&lt;/li&gt;
&lt;li&gt;display current execution mode.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But Telegram handlers should not duplicate execution logic.&lt;/p&gt;

&lt;p&gt;A Telegram action should call the same engine used by TradingView.&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;signal&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;symbol&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;selected_symbol&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CLOSE_ALL&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;source&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;telegram&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;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;trade_engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;process_signal&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That produces one execution path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TradingView ─┐
             ├──► Trade Execution Engine
Signal Engine┤
             │
Telegram ────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One engine.&lt;/p&gt;

&lt;p&gt;One set of rules.&lt;/p&gt;

&lt;p&gt;One state model.&lt;/p&gt;

&lt;p&gt;That consistency becomes increasingly valuable as the platform grows.&lt;/p&gt;




&lt;h2&gt;
  
  
  Logging the Execution Pipeline
&lt;/h2&gt;

&lt;p&gt;A good execution log should answer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;what signal arrived;&lt;/li&gt;
&lt;li&gt;where it came from;&lt;/li&gt;
&lt;li&gt;why it was accepted or rejected;&lt;/li&gt;
&lt;li&gt;what order was submitted;&lt;/li&gt;
&lt;li&gt;what exchange response was returned;&lt;/li&gt;
&lt;li&gt;what position state was created;&lt;/li&gt;
&lt;li&gt;which execution mode was active.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A practical log flow might look 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;[TRADE ENGINE] signal={
  'symbol': 'BTCUSDT',
  'action': 'SELL_FUTURES',
  'source': 'tradingview'
}

[TRADE ENGINE] pair enabled: BTCUSDT
[TRADE ENGINE] execution mode: testnet
[TRADE ENGINE] opening futures short qty=0.001
[BYBIT FUTURES SHORT] order_id=123456789
[POSITION MANAGER] position created: BTCUSDT short
[WEBSOCKET] subscribed: tickers.BTCUSDT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rejection logs should be equally clear.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[REJECT] Pair disabled: ETHUSDT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[REJECT] Short position already open: BTCUSDT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Readable logs are part of system design, not an afterthought.&lt;/p&gt;




&lt;h2&gt;
  
  
  A More Complete Execution Flow
&lt;/h2&gt;

&lt;p&gt;The complete flow can now be represented 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;TradingView / Signal Engine / Telegram
                  │
                  ▼
          Signal Normalization
                  │
                  ▼
          Action Validation
                  │
                  ▼
        Pair Configuration Lookup
                  │
                  ▼
          Enabled / Permission Check
                  │
                  ▼
            Duplicate Check
                  │
                  ▼
          Current State Inspection
                  │
                  ▼
             Symbol Lock
                  │
                  ▼
       Spot / Futures Route Selection
                  │
                  ▼
          Exchange Request Builder
                  │
                  ▼
      Dry Run / Testnet / Mainnet Adapter
                  │
                  ▼
          Exchange Result Validation
                  │
                  ▼
          Persistent Position State
                  │
                  ▼
         WebSocket Subscription Update
                  │
                  ▼
         Telegram / Logging / Analytics
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This architecture is more complex than calling &lt;code&gt;place_order()&lt;/code&gt; directly.&lt;/p&gt;

&lt;p&gt;That complexity is intentional.&lt;/p&gt;

&lt;p&gt;Every layer exists because a real failure mode appeared or became predictable during development.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Belongs in the Trade Execution Engine
&lt;/h2&gt;

&lt;p&gt;A useful rule is to classify responsibilities by question.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does this signal describe a supported action?
&lt;/h3&gt;

&lt;p&gt;Trade Execution Engine.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is this pair allowed to trade?
&lt;/h3&gt;

&lt;p&gt;Trade Execution Engine and configuration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is a position already open?
&lt;/h3&gt;

&lt;p&gt;Trade Execution Engine and Position Storage.&lt;/p&gt;

&lt;h3&gt;
  
  
  How is a Bybit Futures short submitted?
&lt;/h3&gt;

&lt;p&gt;Exchange Client.&lt;/p&gt;

&lt;h3&gt;
  
  
  Has TP1 been reached?
&lt;/h3&gt;

&lt;p&gt;Position Manager.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is the current BTCUSDT price?
&lt;/h3&gt;

&lt;p&gt;WebSocket Manager.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is the monthly net profit?
&lt;/h3&gt;

&lt;p&gt;Analytics Service.&lt;/p&gt;

&lt;h3&gt;
  
  
  Should the operator receive a notification?
&lt;/h3&gt;

&lt;p&gt;Telegram or Notification Service.&lt;/p&gt;

&lt;p&gt;This separation keeps the engine small enough to reason about.&lt;/p&gt;




&lt;h2&gt;
  
  
  Current Trade Execution Features
&lt;/h2&gt;

&lt;p&gt;At the current stage, the platform execution pipeline supports:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;TradingView webhook signals;&lt;/li&gt;
&lt;li&gt;local Python signal generation;&lt;/li&gt;
&lt;li&gt;explicit Spot BUY actions;&lt;/li&gt;
&lt;li&gt;explicit Futures SHORT actions;&lt;/li&gt;
&lt;li&gt;emergency close operations;&lt;/li&gt;
&lt;li&gt;pair-level enable and disable controls;&lt;/li&gt;
&lt;li&gt;pair-specific quantity and risk settings;&lt;/li&gt;
&lt;li&gt;Dry Run execution;&lt;/li&gt;
&lt;li&gt;Bybit Testnet execution;&lt;/li&gt;
&lt;li&gt;Bybit Mainnet execution mode;&lt;/li&gt;
&lt;li&gt;persistent position state;&lt;/li&gt;
&lt;li&gt;restart recovery;&lt;/li&gt;
&lt;li&gt;dynamic WebSocket subscriptions;&lt;/li&gt;
&lt;li&gt;reverse signal handling;&lt;/li&gt;
&lt;li&gt;rejection of unsupported signals;&lt;/li&gt;
&lt;li&gt;rejection of missing or disabled pairs;&lt;/li&gt;
&lt;li&gt;structured execution results;&lt;/li&gt;
&lt;li&gt;Telegram control integration;&lt;/li&gt;
&lt;li&gt;SQLite-based trade analytics after closure.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some areas continue to evolve, especially duplicate signal idempotency, exchange execution reconciliation, and more advanced handling of partial fills.&lt;/p&gt;

&lt;p&gt;That is expected.&lt;/p&gt;

&lt;p&gt;Production trading software is not finished when the first order succeeds.&lt;/p&gt;

&lt;p&gt;It becomes reliable through repeated testing against failure cases.&lt;/p&gt;




&lt;h2&gt;
  
  
  Engineering Challenges That Appear Next
&lt;/h2&gt;

&lt;p&gt;Once the Trade Execution Engine is stable, the next layer becomes the position lifecycle.&lt;/p&gt;

&lt;p&gt;Opening a position is only the beginning.&lt;/p&gt;

&lt;p&gt;The platform still needs to answer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When is TP1 reached?&lt;/li&gt;
&lt;li&gt;How much quantity should close?&lt;/li&gt;
&lt;li&gt;Should Stop Loss move to Break Even?&lt;/li&gt;
&lt;li&gt;When should trailing become active?&lt;/li&gt;
&lt;li&gt;How are highest and lowest prices tracked?&lt;/li&gt;
&lt;li&gt;How should partial exits be stored?&lt;/li&gt;
&lt;li&gt;What happens after a restart?&lt;/li&gt;
&lt;li&gt;How does the system close the remaining quantity safely?&lt;/li&gt;
&lt;li&gt;How are Spot and Futures lifecycle rules kept separate?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those responsibilities belong to the Position Manager.&lt;/p&gt;

&lt;p&gt;The Trade Execution Engine creates the position.&lt;/p&gt;

&lt;p&gt;The Position Manager keeps it alive.&lt;/p&gt;




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

&lt;p&gt;Building a Trade Execution Engine changed the project more than any individual trading feature.&lt;/p&gt;

&lt;p&gt;Before this layer existed, signals were too closely connected to exchange calls.&lt;/p&gt;

&lt;p&gt;After introducing it, every signal source could use the same execution path.&lt;/p&gt;

&lt;p&gt;TradingView, Telegram, and the local Python signal engine no longer needed to understand Bybit order parameters.&lt;/p&gt;

&lt;p&gt;They only needed to express intent.&lt;/p&gt;

&lt;p&gt;The engine became responsible for the difficult boundary between intent and execution:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;validating actions;&lt;/li&gt;
&lt;li&gt;loading configuration;&lt;/li&gt;
&lt;li&gt;inspecting state;&lt;/li&gt;
&lt;li&gt;preventing invalid transitions;&lt;/li&gt;
&lt;li&gt;routing Spot and Futures operations;&lt;/li&gt;
&lt;li&gt;coordinating reverse signals;&lt;/li&gt;
&lt;li&gt;controlling execution modes;&lt;/li&gt;
&lt;li&gt;persisting position state;&lt;/li&gt;
&lt;li&gt;reporting structured results.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The most important lesson was simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Reliable trading software is not built around the ability to place orders. It is built around the ability to control when an order is allowed, understand what state it creates, and recover when something goes wrong.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The platform described in this series continues to evolve. A technical overview of the current project is available here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://py-dev.top/application-software/bybit-signal-trading-bot"&gt;https://py-dev.top/application-software/bybit-signal-trading-bot&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The link is included for readers who want to see the broader system context. The purpose of this series remains engineering: architecture, implementation decisions, failure modes, and lessons learned while building automated trading infrastructure in Python.&lt;/p&gt;




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

&lt;p&gt;The Trade Execution Engine decides &lt;strong&gt;what&lt;/strong&gt; should happen.&lt;/p&gt;

&lt;p&gt;The next challenge is remembering &lt;strong&gt;what has already happened&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Once the first order is executed, the platform becomes responsible for managing a living trading position that evolves over time.&lt;/p&gt;

&lt;p&gt;In the next article, we'll explore how a production-ready Position Manager coordinates the complete lifecycle of a trade, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;position lifecycle states;&lt;/li&gt;
&lt;li&gt;partial Take Profit;&lt;/li&gt;
&lt;li&gt;multiple Take Profit levels;&lt;/li&gt;
&lt;li&gt;remaining quantity tracking;&lt;/li&gt;
&lt;li&gt;Break Even activation;&lt;/li&gt;
&lt;li&gt;Trailing Stop management;&lt;/li&gt;
&lt;li&gt;Stop Loss transitions;&lt;/li&gt;
&lt;li&gt;highest and lowest price tracking;&lt;/li&gt;
&lt;li&gt;persistent position state;&lt;/li&gt;
&lt;li&gt;recovery after application restart;&lt;/li&gt;
&lt;li&gt;Spot and Futures lifecycle differences.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Along the way, we'll see why a Position Manager is much more than a collection of trading variables—it becomes the system's memory of the trading strategy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Next:&lt;/strong&gt; &lt;em&gt;Building a Production-Ready Position Manager for Algorithmic Trading&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/pydevtop/building-a-production-ready-position-manager-for-algorithmic-trading-55n4"&gt;https://dev.to/pydevtop/building-a-production-ready-position-manager-for-algorithmic-trading-55n4&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Suggested DEV.to Tags
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python
trading
architecture
websockets
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;DEV.to currently allows up to four tags per article, so using a focused set of highly relevant tags generally provides better discoverability than trying to cover every possible keyword.&lt;/p&gt;




&lt;h2&gt;
  
  
  SEO Keywords Covered
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python trade execution engine
algorithmic trading platform
python trading architecture
position manager
state management
event-driven architecture
Bybit API Python
TradingView webhook Python
crypto trading automation
automated trading platform
Spot trading bot
Futures trading bot
Python WebSocket trading
trade signal routing
duplicate trading signals
persistent state
restart recovery
risk management engine
reverse trading signals
Dry Run Testnet Mainnet
Python fintech development
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>python</category>
      <category>websockets</category>
      <category>bybit</category>
      <category>tradingview</category>
    </item>
    <item>
      <title>Building a Python Algorithmic Trading Platform with TradingView, Bybit API and WebSocket</title>
      <dc:creator>PyDev</dc:creator>
      <pubDate>Wed, 15 Jul 2026 11:55:36 +0000</pubDate>
      <link>https://dev.to/pydevtop/building-a-python-algorithmic-trading-platform-with-tradingview-bybit-api-and-websocket-3eb3</link>
      <guid>https://dev.to/pydevtop/building-a-python-algorithmic-trading-platform-with-tradingview-bybit-api-and-websocket-3eb3</guid>
      <description>&lt;p&gt;&lt;em&gt;&lt;strong&gt;Series:&lt;/strong&gt; Engineering an Algorithmic Trading Platform in Python&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Part:&lt;/strong&gt; 1 of 18&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;In this series&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This series documents the engineering decisions behind building a production-ready algorithmic trading platform in Python. Rather than focusing on trading strategies, the articles explore software architecture, trade execution, risk management, real-time data processing, and system reliability.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Most algorithmic trading projects begin with a deceptively simple idea.&lt;/p&gt;

&lt;p&gt;Connect &lt;strong&gt;TradingView&lt;/strong&gt; to an exchange API, receive webhook signals, execute market orders, and let automation handle the rest.&lt;/p&gt;

&lt;p&gt;On paper, the architecture seems almost trivial.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TradingView
      │
Webhook
      │
Python Script
      │
Exchange API
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After all, sending a POST request and placing a market order only takes a few lines of Python.&lt;/p&gt;

&lt;p&gt;That assumption lasted exactly until the first real trading scenarios appeared.&lt;/p&gt;

&lt;p&gt;The moment open positions had to survive an application restart, partial profits needed to be tracked correctly, stop losses had to move automatically to break even, and live market prices started arriving several times per second, the project stopped looking like a "trading bot."&lt;/p&gt;

&lt;p&gt;It became a distributed software system with multiple independent components that had to cooperate reliably under constantly changing market conditions.&lt;/p&gt;

&lt;p&gt;Over the past several months I've been building a modular algorithmic trading platform in Python. The original goal was never to create another "buy and sell" script. Instead, I wanted an architecture that could evolve over time without constantly rewriting core components every time a new feature appeared.&lt;/p&gt;

&lt;p&gt;This article describes the engineering decisions behind that architecture, the problems that emerged during development, and why separating responsibilities between components made the system significantly easier to maintain.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Most Trading Bots Eventually Become Unmaintainable
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A trading bot becomes difficult to maintain when every feature is implemented inside a single execution flow.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;One pattern appears repeatedly in open-source trading bots.&lt;/p&gt;

&lt;p&gt;Everything happens inside one large function.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A webhook arrives.&lt;/li&gt;
&lt;li&gt;The exchange API is called.&lt;/li&gt;
&lt;li&gt;The position is updated.&lt;/li&gt;
&lt;li&gt;Risk checks are performed.&lt;/li&gt;
&lt;li&gt;Analytics are calculated.&lt;/li&gt;
&lt;li&gt;Notifications are sent.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Eventually thousands of lines of business logic end up sharing the same execution flow.&lt;/p&gt;

&lt;p&gt;Initially this approach feels productive because features can be added quickly.&lt;/p&gt;

&lt;p&gt;Later, every modification becomes risky.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adding a trailing stop unexpectedly breaks take-profit logic.&lt;/li&gt;
&lt;li&gt;Implementing futures trading affects spot trading.&lt;/li&gt;
&lt;li&gt;Introducing Telegram controls changes execution behaviour.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of building another bot, I decided to build a platform.&lt;/p&gt;




&lt;h2&gt;
  
  
  From a Trading Bot to a Trading Platform
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                    TradingView
                          │
                    FastAPI Webhook
                          │
               Trade Execution Engine
                          │
                  Risk Management
                          │
                 Position Manager
                 ┌────────┴────────┐
                 │                 │
          Spot Execution    Futures Execution
                 │                 │
                 └────────┬────────┘
                          │
                       Bybit API
                          │
                  Analytics Engine
                          │
               Telegram Control Panel
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each layer has a single responsibility.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Webhook&lt;/strong&gt; validates requests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trade Execution Engine&lt;/strong&gt; interprets signals.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Risk Manager&lt;/strong&gt; applies trading rules.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Position Manager&lt;/strong&gt; owns the lifecycle of positions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Execution Layer&lt;/strong&gt; communicates with the exchange.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Analytics&lt;/strong&gt; processes completed trades.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Telegram&lt;/strong&gt; provides operational control.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This separation dramatically reduced complexity as the project evolved.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why the Webhook Should Never Execute Orders Directly
&lt;/h2&gt;

&lt;p&gt;Many tutorials show something similar to this:&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="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;def&lt;/span&gt; &lt;span class="nf"&gt;webhook&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;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;place_order&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While this is sufficient for a demo, production systems quickly expose its limitations.&lt;/p&gt;

&lt;p&gt;Questions appear immediately.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What if TradingView sends the same webhook twice?&lt;/li&gt;
&lt;li&gt;What if the exchange rejects the order?&lt;/li&gt;
&lt;li&gt;What if another position is already open?&lt;/li&gt;
&lt;li&gt;What if the signal arrives after market conditions have changed?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead, the webhook performs only three responsibilities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Validate incoming requests.&lt;/li&gt;
&lt;li&gt;Verify authentication and secrets.&lt;/li&gt;
&lt;li&gt;Forward a normalized signal to the Trade Execution Engine.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Business logic belongs elsewhere.&lt;/p&gt;




&lt;h2&gt;
  
  
  Designing the Trade Execution Engine
&lt;/h2&gt;

&lt;p&gt;The Trade Execution Engine became the heart of the platform.&lt;/p&gt;

&lt;p&gt;Its responsibility is intentionally narrow.&lt;/p&gt;

&lt;p&gt;It does &lt;strong&gt;not&lt;/strong&gt; calculate indicators.&lt;/p&gt;

&lt;p&gt;It does &lt;strong&gt;not&lt;/strong&gt; generate analytics.&lt;/p&gt;

&lt;p&gt;It does &lt;strong&gt;not&lt;/strong&gt; manage Telegram.&lt;/p&gt;

&lt;p&gt;It simply transforms validated signals into trading actions.&lt;/p&gt;

&lt;p&gt;Typical actions include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;BUY_SPOT&lt;/li&gt;
&lt;li&gt;SELL_FUTURES&lt;/li&gt;
&lt;li&gt;CLOSE_POSITION&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before dispatching an order, the engine checks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is the trading pair enabled?&lt;/li&gt;
&lt;li&gt;Is there already an open position?&lt;/li&gt;
&lt;li&gt;Has this signal already been processed?&lt;/li&gt;
&lt;li&gt;Does the current application state allow execution?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Only then does the execution layer communicate with the exchange.&lt;/p&gt;




&lt;h2&gt;
  
  
  About the Project
&lt;/h2&gt;

&lt;p&gt;The examples shown throughout this series are taken from a personal algorithmic trading platform that I've been building and refining over the past several months.&lt;/p&gt;

&lt;p&gt;The project continues to evolve as new engineering challenges appear during development.&lt;/p&gt;

&lt;p&gt;Additional information about the platform is available here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://py-dev.top/application-software/bybit-signal-trading-bot"&gt;https://py-dev.top/application-software/bybit-signal-trading-bot&lt;/a&gt;&lt;/p&gt;




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

&lt;p&gt;In the next chapter, we move from the high-level platform architecture to one of its most important components—the &lt;strong&gt;Trade Execution Engine&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We'll explore how trading signals are transformed into controlled execution decisions, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Signal normalization&lt;/li&gt;
&lt;li&gt;Action routing&lt;/li&gt;
&lt;li&gt;Pair configuration&lt;/li&gt;
&lt;li&gt;Duplicate signal protection&lt;/li&gt;
&lt;li&gt;State-aware execution&lt;/li&gt;
&lt;li&gt;Spot and Futures routing&lt;/li&gt;
&lt;li&gt;Reverse signal handling&lt;/li&gt;
&lt;li&gt;Dry Run, Testnet, and Mainnet execution&lt;/li&gt;
&lt;li&gt;Error handling and execution flow&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you enjoyed this article, continue with Part 2:&lt;/p&gt;

&lt;p&gt;➡️ &lt;strong&gt;Designing a Modular Trade Execution Engine in Python&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/pydevtop/designing-a-modular-trade-execution-engine-in-python-5ebh"&gt;https://dev.to/pydevtop/designing-a-modular-trade-execution-engine-in-python-5ebh&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>trading</category>
      <category>bybit</category>
      <category>tradingview</category>
    </item>
    <item>
      <title>I Kept Missing Deals on Binance P2P — Until I Fixed the Real Problem</title>
      <dc:creator>PyDev</dc:creator>
      <pubDate>Thu, 23 Apr 2026 08:45:32 +0000</pubDate>
      <link>https://dev.to/pydevtop/i-kept-missing-deals-on-binance-p2p-until-i-fixed-the-real-problem-afn</link>
      <guid>https://dev.to/pydevtop/i-kept-missing-deals-on-binance-p2p-until-i-fixed-the-real-problem-afn</guid>
      <description>&lt;p&gt;I kept missing deals on Binance P2P.&lt;/p&gt;

&lt;p&gt;And at some point, I realized:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;every missed deal is just lost money.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Not because of bad prices.&lt;br&gt;
Not because of competition.&lt;/p&gt;

&lt;p&gt;But because I was too slow.&lt;/p&gt;


&lt;h2&gt;
  
  
  🧠 The problem nobody talks about
&lt;/h2&gt;

&lt;p&gt;When people discuss Binance P2P trading, they focus on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;pricing strategies&lt;/li&gt;
&lt;li&gt;arbitrage&lt;/li&gt;
&lt;li&gt;ads&lt;/li&gt;
&lt;li&gt;spreads&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But almost nobody talks about this:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;execution speed&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here’s what actually happens:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;buyer sends a message&lt;/li&gt;
&lt;li&gt;you don’t see it immediately&lt;/li&gt;
&lt;li&gt;buyer pays&lt;/li&gt;
&lt;li&gt;you react too late&lt;/li&gt;
&lt;li&gt;buyer loses trust&lt;/li&gt;
&lt;li&gt;deal goes bad or gets canceled&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All because of seconds.&lt;/p&gt;


&lt;h2&gt;
  
  
  🔁 My workflow was broken
&lt;/h2&gt;

&lt;p&gt;My setup looked like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Binance open in one tab&lt;/li&gt;
&lt;li&gt;Telegram in another&lt;/li&gt;
&lt;li&gt;constant refreshing&lt;/li&gt;
&lt;li&gt;switching between windows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It felt like I was working…&lt;/p&gt;

&lt;p&gt;But in reality, I was &lt;strong&gt;lagging behind every deal&lt;/strong&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  💡 The realization
&lt;/h2&gt;

&lt;p&gt;The problem wasn’t strategy.&lt;/p&gt;

&lt;p&gt;The problem was friction.&lt;/p&gt;

&lt;p&gt;Too many steps between:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;message → reply&lt;/li&gt;
&lt;li&gt;payment → release&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And every extra step = delay.&lt;/p&gt;


&lt;h2&gt;
  
  
  ⚡ I didn’t optimize price first — I optimized execution
&lt;/h2&gt;

&lt;p&gt;Instead of building another dashboard…&lt;/p&gt;

&lt;p&gt;I built something simpler:&lt;/p&gt;

&lt;p&gt;👉 move the entire P2P workflow into Telegram&lt;/p&gt;


&lt;h2&gt;
  
  
  💬 Binance Chat → Telegram
&lt;/h2&gt;

&lt;p&gt;Now every message appears instantly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;💬 Binance chat
#22880241441543839744
buyer: I sent the payment
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No refresh. No tab switching.&lt;/p&gt;

&lt;p&gt;Just real-time flow.&lt;/p&gt;




&lt;h2&gt;
  
  
  ✍️ Reply instantly
&lt;/h2&gt;

&lt;p&gt;I don’t open Binance chat anymore.&lt;/p&gt;

&lt;p&gt;I reply directly from Telegram —&lt;br&gt;
and the message goes back to Binance.&lt;/p&gt;

&lt;p&gt;Fast. Clean. No friction.&lt;/p&gt;


&lt;h2&gt;
  
  
  💰 The moment that actually matters
&lt;/h2&gt;

&lt;p&gt;The most important moment in P2P:&lt;/p&gt;

&lt;p&gt;👉 when the buyer marks the order as paid&lt;/p&gt;

&lt;p&gt;Before:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I had to notice it manually&lt;/li&gt;
&lt;li&gt;open the order&lt;/li&gt;
&lt;li&gt;verify everything&lt;/li&gt;
&lt;li&gt;click release&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now:&lt;/p&gt;

&lt;p&gt;The bot detects it instantly and shows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Order paid&lt;/li&gt;
&lt;li&gt;👤 Counterparty&lt;/li&gt;
&lt;li&gt;💰 &lt;strong&gt;Release Crypto button&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  ⚡ One click → deal closed
&lt;/h2&gt;

&lt;p&gt;I press one button:&lt;/p&gt;

&lt;p&gt;👉 💰 Release Crypto&lt;/p&gt;

&lt;p&gt;And the bot executes:&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;ReleaseCrypto&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;order_number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pay_id&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;body&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;orderNumber&lt;/span&gt;&lt;span class="sh"&gt;"&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;order_number&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;pay_id&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;body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;payId&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="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pay_id&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;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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getServerTime&lt;/span&gt;&lt;span class="p"&gt;()&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;signature&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSignatureTrader&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;headers&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;X-MBX-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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getApiKey&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;clientType&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;web&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;Content-Type&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;application/json&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;response&lt;/span&gt; &lt;span class="o"&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://api.binance.com/sapi/v1/c2c/orderMatch/releaseCoin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&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;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;body&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;response&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🚀 Real result
&lt;/h2&gt;

&lt;p&gt;Crypto is released in &lt;strong&gt;under a second&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;No UI.&lt;br&gt;
No searching for the order.&lt;br&gt;
No extra steps.&lt;/p&gt;

&lt;p&gt;Just action → done.&lt;/p&gt;




&lt;h2&gt;
  
  
  📈 But here’s the second layer
&lt;/h2&gt;

&lt;p&gt;Speed alone is not enough.&lt;/p&gt;

&lt;p&gt;You also need to stay competitive on price.&lt;/p&gt;

&lt;p&gt;So I added:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;auto price tracking (stepper logic)&lt;/strong&gt;&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;monitors competitors&lt;/li&gt;
&lt;li&gt;adjusts price dynamically&lt;/li&gt;
&lt;li&gt;stays in the top range&lt;/li&gt;
&lt;li&gt;reacts to market changes&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🧠 What this actually becomes
&lt;/h2&gt;

&lt;p&gt;Not just a bot.&lt;/p&gt;

&lt;p&gt;But a &lt;strong&gt;full P2P trading workflow system&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;📊 price tracking&lt;/li&gt;
&lt;li&gt;💬 chat handling&lt;/li&gt;
&lt;li&gt;⚡ instant replies&lt;/li&gt;
&lt;li&gt;💰 fast crypto release&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🆚 Compared to typical P2P bots
&lt;/h2&gt;

&lt;p&gt;Most bots focus on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;repricing&lt;/li&gt;
&lt;li&gt;ad management&lt;/li&gt;
&lt;li&gt;dashboards&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This setup combines:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;price competitiveness + execution speed&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Which is what actually wins deals.&lt;/p&gt;




&lt;h2&gt;
  
  
  🌍 Bonus
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;multi-language support&lt;/li&gt;
&lt;li&gt;receipt (image) handling&lt;/li&gt;
&lt;li&gt;clean Telegram interface&lt;/li&gt;
&lt;li&gt;no unnecessary noise&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  ⚠️ Important
&lt;/h2&gt;

&lt;p&gt;This is not blind automation.&lt;/p&gt;

&lt;p&gt;And not auto-release.&lt;/p&gt;

&lt;p&gt;Everything is:&lt;/p&gt;

&lt;p&gt;👉 manual decision&lt;br&gt;
👉 instant execution&lt;/p&gt;




&lt;h2&gt;
  
  
  🔥 Final thought
&lt;/h2&gt;

&lt;p&gt;I didn’t need a smarter strategy.&lt;/p&gt;

&lt;p&gt;I needed a faster system.&lt;/p&gt;

&lt;p&gt;Because in P2P:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;price gets you the deal&lt;br&gt;
speed closes it&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;I kept missing deals on Binance P2P&lt;br&gt;
because I was too slow.&lt;/p&gt;

&lt;p&gt;So I built a system that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;follows market price&lt;/li&gt;
&lt;li&gt;syncs chat to Telegram&lt;/li&gt;
&lt;li&gt;lets me reply instantly&lt;/li&gt;
&lt;li&gt;detects payments&lt;/li&gt;
&lt;li&gt;and releases crypto in one click&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Now I don’t miss those moments anymore.
&lt;/h2&gt;

&lt;h2&gt;
  
  
  🚀 Try the bot
&lt;/h2&gt;

&lt;p&gt;If you're interested in this workflow or want to test it:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://py-dev.top/application-software/p2p-binance-telegram-bot"&gt;https://py-dev.top/application-software/p2p-binance-telegram-bot&lt;/a&gt;&lt;/p&gt;

</description>
      <category>binance</category>
      <category>p2p</category>
      <category>python</category>
      <category>binancep2pbot</category>
    </item>
    <item>
      <title>Binance P2P Order History Fetcher with Full Name — Why I Built It (and Why Binance Export Is Useless for Accounting)</title>
      <dc:creator>PyDev</dc:creator>
      <pubDate>Thu, 02 Apr 2026 11:25:10 +0000</pubDate>
      <link>https://dev.to/pydevtop/binance-p2p-order-history-fetcher-with-full-name-why-i-built-it-and-why-binance-export-is-5f8h</link>
      <guid>https://dev.to/pydevtop/binance-p2p-order-history-fetcher-with-full-name-why-i-built-it-and-why-binance-export-is-5f8h</guid>
      <description>&lt;h2&gt;
  
  
  How it started
&lt;/h2&gt;

&lt;p&gt;A while ago I ran into a very practical problem.&lt;/p&gt;

&lt;p&gt;I was working with P2P transactions on Binance and needed to prepare reports — not for myself, but for accounting.&lt;/p&gt;

&lt;p&gt;At first I thought:&lt;br&gt;&lt;br&gt;
“Okay, Binance already has export. This should be easy.”&lt;/p&gt;

&lt;p&gt;So I downloaded the P2P order history.&lt;/p&gt;

&lt;p&gt;And… that’s where the problem started.&lt;/p&gt;




&lt;h2&gt;
  
  
  The moment I realized something is wrong
&lt;/h2&gt;

&lt;p&gt;The export looked fine at first glance.&lt;/p&gt;

&lt;p&gt;But then I noticed something critical:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;All names and surnames were hidden (&lt;/strong&gt;&lt;em&gt;).&lt;/em&gt;*&lt;/p&gt;

&lt;p&gt;And that completely breaks everything.&lt;/p&gt;

&lt;p&gt;Because for accounting, compliance, or any kind of reporting —&lt;br&gt;&lt;br&gt;
you NEED full names of counterparties.&lt;/p&gt;

&lt;p&gt;Without that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you can’t validate transactions
&lt;/li&gt;
&lt;li&gt;you can’t build proper reports
&lt;/li&gt;
&lt;li&gt;you can’t submit documents
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Basically, the export becomes &lt;strong&gt;useless&lt;/strong&gt;.&lt;/p&gt;




&lt;p&gt;If you’ve ever tried to use Binance export for accounting — you already know the pain.&lt;/p&gt;

&lt;p&gt;I ended up building a tool that actually solves this problem:&lt;br&gt;&lt;br&gt;
👉 &lt;a href="https://py-dev.top/application-software/binance-p2p-order-history-fetcher-with-full-name"&gt;https://py-dev.top/application-software/binance-p2p-order-history-fetcher-with-full-name&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  “But Binance gives it for free…”
&lt;/h2&gt;

&lt;p&gt;Yes, Binance does provide a free export.&lt;/p&gt;

&lt;p&gt;But let’s be honest:&lt;/p&gt;

&lt;p&gt;👉 It’s not usable for any serious purpose.&lt;/p&gt;

&lt;p&gt;The data is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;incomplete
&lt;/li&gt;
&lt;li&gt;anonymized
&lt;/li&gt;
&lt;li&gt;not suitable for accounting
&lt;/li&gt;
&lt;li&gt;impossible to properly process
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s more like a &lt;strong&gt;viewer&lt;/strong&gt;, not a real export.&lt;/p&gt;

&lt;p&gt;And this is not a bug — it’s intentional.&lt;br&gt;&lt;br&gt;
Binance simply does not expose full names in their export.&lt;/p&gt;




&lt;h2&gt;
  
  
  The idea: what if I just automate it?
&lt;/h2&gt;

&lt;p&gt;At that point I thought:&lt;/p&gt;

&lt;p&gt;“Okay, I’ll just write a small script to fix this.”&lt;/p&gt;

&lt;p&gt;Spoiler:&lt;br&gt;&lt;br&gt;
👉 It was &lt;strong&gt;NOT&lt;/strong&gt; a small script.&lt;/p&gt;




&lt;h2&gt;
  
  
  The reality of building this tool
&lt;/h2&gt;

&lt;p&gt;What seems simple at first actually involves:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;working around Binance API limitations
&lt;/li&gt;
&lt;li&gt;handling authentication and requests properly
&lt;/li&gt;
&lt;li&gt;collecting full order data (not partial)
&lt;/li&gt;
&lt;li&gt;extracting real *&lt;em&gt;full names instead of *&lt;/em&gt;*
&lt;/li&gt;
&lt;li&gt;structuring everything correctly
&lt;/li&gt;
&lt;li&gt;exporting clean Excel reports
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And most importantly — making it &lt;strong&gt;stable&lt;/strong&gt;, not something that breaks after 2 runs.&lt;/p&gt;

&lt;p&gt;That’s how &lt;strong&gt;Binance P2P Order History Fetcher with Full Name&lt;/strong&gt; was created.&lt;/p&gt;




&lt;h2&gt;
  
  
  What the tool actually does
&lt;/h2&gt;

&lt;p&gt;This is a Python-based tool that:&lt;/p&gt;

&lt;p&gt;✔ fetches &lt;strong&gt;complete P2P order history&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
✔ retrieves &lt;strong&gt;full names and surnames (no masking)&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
✔ exports everything into a &lt;strong&gt;clean Excel file&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
✔ works for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;full year
&lt;/li&gt;
&lt;li&gt;specific months
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What data you get
&lt;/h2&gt;

&lt;p&gt;The generated Excel file includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;orderNumber — order ID
&lt;/li&gt;
&lt;li&gt;advNo — advertisement ID
&lt;/li&gt;
&lt;li&gt;tradeType — buy/sell
&lt;/li&gt;
&lt;li&gt;asset — crypto (USDT, BTC, etc.)
&lt;/li&gt;
&lt;li&gt;fiat — fiat currency (USD, EUR…)
&lt;/li&gt;
&lt;li&gt;amount — crypto amount
&lt;/li&gt;
&lt;li&gt;totalPrice — total fiat value
&lt;/li&gt;
&lt;li&gt;unitPrice — price per unit
&lt;/li&gt;
&lt;li&gt;orderStatus — status
&lt;/li&gt;
&lt;li&gt;commission — fees
&lt;/li&gt;
&lt;li&gt;takerAmount — final crypto amount
&lt;/li&gt;
&lt;li&gt;payMethodName — payment method
&lt;/li&gt;
&lt;li&gt;Match time (UTC) — timestamp
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And the most important part:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;Full Name (e.g., John Smith)&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Why this actually matters
&lt;/h2&gt;

&lt;p&gt;Let’s be clear.&lt;/p&gt;

&lt;p&gt;Without full names:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;reports are incomplete
&lt;/li&gt;
&lt;li&gt;accounting becomes manual hell
&lt;/li&gt;
&lt;li&gt;audits become risky
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With full names:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you can generate real reports
&lt;/li&gt;
&lt;li&gt;you can use the data in accounting systems
&lt;/li&gt;
&lt;li&gt;you can actually &lt;strong&gt;TRUST&lt;/strong&gt; your export
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  “It’s just a script, why is it paid?”
&lt;/h2&gt;

&lt;p&gt;I literally got this comment yesterday:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Binance gives it for free, this isn’t worth the money.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here’s the honest answer:&lt;/p&gt;

&lt;p&gt;👉 If you think it’s “just a script” — try to build it.&lt;/p&gt;

&lt;p&gt;Deal with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API quirks
&lt;/li&gt;
&lt;li&gt;missing fields
&lt;/li&gt;
&lt;li&gt;hidden data
&lt;/li&gt;
&lt;li&gt;pagination
&lt;/li&gt;
&lt;li&gt;data consistency
&lt;/li&gt;
&lt;li&gt;Excel formatting
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then come back and say it’s “free”.&lt;/p&gt;

&lt;p&gt;Because the real value here is not the code itself —&lt;br&gt;&lt;br&gt;
👉 it’s solving a problem Binance doesn’t solve.&lt;/p&gt;




&lt;h2&gt;
  
  
  Installation (quick overview)
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Install Python&lt;br&gt;&lt;br&gt;
👉 &lt;a href="https://py-dev.top/installing-python"&gt;https://py-dev.top/installing-python&lt;/a&gt;  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Extract the archive to &lt;code&gt;C:\&lt;/code&gt;  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Open &lt;code&gt;config.ini&lt;/code&gt; and configure parameters  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Run &lt;code&gt;start-the-program.bat&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;Binance export is “free”.&lt;/p&gt;

&lt;p&gt;But in reality:&lt;br&gt;&lt;br&gt;
👉 it’s incomplete and unusable for real workflows.&lt;/p&gt;

&lt;p&gt;If you only need to “look at history” — it’s fine.&lt;/p&gt;

&lt;p&gt;If you need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;accounting
&lt;/li&gt;
&lt;li&gt;reporting
&lt;/li&gt;
&lt;li&gt;real data
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then you need something more.&lt;/p&gt;

&lt;p&gt;That’s exactly why I built this tool.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>P2P Trade Bot for Binance – Smart Automation for P2P Traders</title>
      <dc:creator>PyDev</dc:creator>
      <pubDate>Mon, 29 Sep 2025 13:00:22 +0000</pubDate>
      <link>https://dev.to/pydevtop/p2p-trade-bot-for-binance-smart-automation-for-p2p-traders-5d5j</link>
      <guid>https://dev.to/pydevtop/p2p-trade-bot-for-binance-smart-automation-for-p2p-traders-5d5j</guid>
      <description>&lt;h1&gt;
  
  
  The Engineering Behind a Binance P2P Price Tracking Bot: Automation &amp;amp; Execution Logic
&lt;/h1&gt;

&lt;p&gt;In the fast-paced world of &lt;strong&gt;Binance P2P trading&lt;/strong&gt;, maintaining a competitive position requires constant monitoring of market conditions, competitor pricing, and ad visibility.&lt;/p&gt;

&lt;p&gt;Manual price updates are not only inefficient but also introduce latency — and in P2P markets, even small delays can result in lost trades or reduced profitability.&lt;/p&gt;

&lt;p&gt;This article explores the internal logic and automation principles behind &lt;strong&gt;P2PTradeBot&lt;/strong&gt;, a system designed to optimize ad positioning and price competitiveness on Binance P2P.&lt;/p&gt;




&lt;h2&gt;
  
  
  ⚙️ The Core Problem: Manual Price Tracking
&lt;/h2&gt;

&lt;p&gt;Most P2P traders face the same challenges:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Continuous monitoring of competitor ads&lt;/li&gt;
&lt;li&gt;Manual price adjustments&lt;/li&gt;
&lt;li&gt;Delays in reacting to market changes&lt;/li&gt;
&lt;li&gt;Difficulty maintaining top positions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As market activity increases, manual interaction becomes a bottleneck.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧠 Automated Price Tracking Engine
&lt;/h2&gt;

&lt;p&gt;At the core of P2PTradeBot is a &lt;strong&gt;real-time price tracking system&lt;/strong&gt; that continuously analyzes competitor ads and adjusts pricing dynamically.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conceptual Logic:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Monitor selected competitors or full order book&lt;/li&gt;
&lt;li&gt;Detect price changes in real time&lt;/li&gt;
&lt;li&gt;Calculate optimal price based on strategy&lt;/li&gt;
&lt;li&gt;Update ad price within predefined limits&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This ensures that ads remain competitive without entering uncontrolled price drops.&lt;/p&gt;




&lt;h2&gt;
  
  
  🏗️ Execution Model
&lt;/h2&gt;

&lt;p&gt;The system operates through three main components:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Market Monitoring Layer
&lt;/h3&gt;

&lt;p&gt;Continuously fetches and analyzes Binance P2P ads, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prices&lt;/li&gt;
&lt;li&gt;Volumes&lt;/li&gt;
&lt;li&gt;Payment methods&lt;/li&gt;
&lt;li&gt;Seller types&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Decision Engine
&lt;/h3&gt;

&lt;p&gt;Applies user-defined rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Price thresholds&lt;/li&gt;
&lt;li&gt;Competitor targeting&lt;/li&gt;
&lt;li&gt;Volume filters&lt;/li&gt;
&lt;li&gt;Payment preferences&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Execution Layer
&lt;/h3&gt;

&lt;p&gt;Handles automated price updates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keeps ads at Top 1 position&lt;/li&gt;
&lt;li&gt;Applies safe price boundaries&lt;/li&gt;
&lt;li&gt;Prevents unnecessary updates&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🚀 Key Automation Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Auto Top Positioning (Top 1)&lt;/strong&gt;&lt;br&gt;
Maintains leading position in P2P listings&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Competitor Price Tracking&lt;/strong&gt;&lt;br&gt;
Reacts instantly to competitor price changes&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Dynamic Filters&lt;/strong&gt;&lt;br&gt;
Select ads by volume, payment method, or seller type&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Price Protection Logic&lt;/strong&gt;&lt;br&gt;
Prevents overpricing or aggressive undercutting&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Multi-Ad Processing&lt;/strong&gt;&lt;br&gt;
Supports multiple listings simultaneously&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  📈 Performance &amp;amp; Efficiency
&lt;/h2&gt;

&lt;p&gt;By automating price updates, the system:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reduces manual workload&lt;/li&gt;
&lt;li&gt;Minimizes reaction latency&lt;/li&gt;
&lt;li&gt;Maintains consistent market positioning&lt;/li&gt;
&lt;li&gt;Improves execution speed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This allows traders to operate efficiently even in high-frequency environments.&lt;/p&gt;




&lt;h2&gt;
  
  
  🛡️ Stability &amp;amp; Control
&lt;/h2&gt;

&lt;p&gt;To ensure safe operation, the system includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Controlled update frequency&lt;/li&gt;
&lt;li&gt;Filtering of irrelevant or inactive ads&lt;/li&gt;
&lt;li&gt;Configurable price limits&lt;/li&gt;
&lt;li&gt;Separation between logic and execution&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This helps maintain stable behavior under varying market conditions.&lt;/p&gt;




&lt;h2&gt;
  
  
  🌐 Practical Use Case
&lt;/h2&gt;

&lt;p&gt;A trader managing multiple Binance P2P ads can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Automatically track competitors&lt;/li&gt;
&lt;li&gt;Maintain top listing positions&lt;/li&gt;
&lt;li&gt;Apply custom filters for payment methods&lt;/li&gt;
&lt;li&gt;Adjust pricing strategy without manual input&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  ⚡ Conclusion
&lt;/h2&gt;

&lt;p&gt;Automation is no longer optional in Binance P2P trading.&lt;/p&gt;

&lt;p&gt;Systems like &lt;strong&gt;P2PTradeBot&lt;/strong&gt; allow traders to eliminate manual inefficiencies, maintain competitive pricing, and scale operations without constant supervision.&lt;/p&gt;

&lt;p&gt;By combining real-time monitoring with rule-based execution, traders can achieve consistent performance in dynamic P2P markets.&lt;/p&gt;




&lt;p&gt;Explore the project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/pydevtop/p2p-trade-bot-binance" rel="noopener noreferrer"&gt;https://github.com/pydevtop/p2p-trade-bot-binance&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Website: &lt;a href="https://py-dev.top/application-software/p2p-trade-bot-binance"&gt;https://py-dev.top/application-software/p2p-trade-bot-binance&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>binance</category>
      <category>p2p</category>
      <category>python</category>
      <category>binancep2pbot</category>
    </item>
    <item>
      <title>P2P Trade Bot for Binance – Automating Your Crypto Trading</title>
      <dc:creator>PyDev</dc:creator>
      <pubDate>Fri, 26 Sep 2025 15:00:24 +0000</pubDate>
      <link>https://dev.to/pydevtop/p2p-trade-bot-for-binance-automating-your-crypto-trading-8e9</link>
      <guid>https://dev.to/pydevtop/p2p-trade-bot-for-binance-automating-your-crypto-trading-8e9</guid>
      <description>&lt;p&gt;Managing P2P ads on Binance manually can be time-consuming and inefficient. The Binance P2P Trade Bot solves this by automating pricing, monitoring competitors, and keeping your ads optimized for maximum visibility and profitability.&lt;/p&gt;

&lt;p&gt;For traders who want to automate P2P ads, check out the Binance bot here:&lt;br&gt;&lt;br&gt;
👉 &lt;a href="https://arbitrage-scanner.io/crypto-trading-bot/p2p-trade-bot-binance" rel="noopener noreferrer"&gt;https://arbitrage-scanner.io/crypto-trading-bot/p2p-trade-bot-binance&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🔧 &lt;strong&gt;How the P2P Trade Bot Works&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The bot constantly tracks competitor prices and adjusts your ads in real time.&lt;br&gt;
This “Step / Follow Competitor” mechanism ensures your offers always remain attractive to buyers and sellers, while staying within the rules you define.&lt;/p&gt;

&lt;p&gt;You set the strategy → the bot keeps your ads updated → you stay ahead in the order book.&lt;/p&gt;

&lt;p&gt;🚀 &lt;strong&gt;Key Features&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Multi-Processor Support&lt;br&gt;
Efficient handling of large datasets and background tasks without delays.&lt;/p&gt;

&lt;p&gt;Custom Seller Tracking&lt;br&gt;
Choose to monitor a specific trader, only verified merchants, or all available sellers — with filters for minimum trade size.&lt;/p&gt;

&lt;p&gt;Advanced Ad Control&lt;br&gt;
Configure each ad with flexible criteria to maximize profit and precision.&lt;/p&gt;

&lt;p&gt;⚡ &lt;strong&gt;Powerful Functions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AutoPilot (Top Position) → Keeps your ad at the very top of the P2P order book automatically.&lt;/p&gt;

&lt;p&gt;Competitor Tracking → Dynamically adjusts your ad to match or beat chosen sellers.&lt;/p&gt;

&lt;p&gt;Volume Filter → Targets ads based on transaction size.&lt;/p&gt;

&lt;p&gt;Price Thresholds → Prevents pricing above/below your defined limits.&lt;/p&gt;

&lt;p&gt;Payment Method Filter → Shows only ads with selected payment options.&lt;/p&gt;

&lt;p&gt;Merchant/User Filter → Focus trading on verified merchants or all users.&lt;/p&gt;

&lt;p&gt;Payment Time Filter → Exclude offers with slow settlement times.&lt;/p&gt;

&lt;p&gt;Blacklist Feature → Block specific sellers by nickname so the bot ignores them.&lt;/p&gt;

&lt;p&gt;💡 &lt;strong&gt;Why Use the Binance P2P Trade Bot?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This tool combines automation, competitor analysis, and granular filters to make your P2P trading faster, safer, and more profitable.&lt;br&gt;
Whether you run ads manually or want full automation, the bot ensures your positions are always optimized to market conditions.&lt;/p&gt;

&lt;p&gt;🛠 &lt;strong&gt;Installation Guide&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Setting up the bot is straightforward. You’ll just need a Telegram bot and Python environment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Create a Telegram Bot&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Register a new bot via &lt;a href="https://t.me/BotFather" rel="noopener noreferrer"&gt;@BotFather&lt;/a&gt; on Telegram.&lt;/p&gt;

&lt;p&gt;Copy the API key and paste it into config.ini inside the P2PTradeBot folder:&lt;/p&gt;

&lt;p&gt;BOT_TOKEN = 11111111:111111111&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Install Python&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Download and install Python from the official website&lt;br&gt;
.&lt;/p&gt;

&lt;p&gt;Unpack P2PTradeBot.zip to a folder (e.g., C:\P2PTradeBot).&lt;/p&gt;

&lt;p&gt;Open config.ini and edit the parameters according to your trading setup.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Install Dependencies&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Inside the bot folder, run:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;install.bat&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This installs all required Python packages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Start the Bot&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;code&gt;start.bat&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Your Binance P2P Trade Bot will launch and begin executing trades automatically based on your configuration.&lt;/p&gt;

&lt;p&gt;✅ With this bot, you no longer need to babysit your ads.&lt;br&gt;
✅ Just configure once, and let automation do the heavy lifting.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How to Update Binance P2P Ad Price with Python: Complete Step-by-Step Tutorial</title>
      <dc:creator>PyDev</dc:creator>
      <pubDate>Mon, 22 Sep 2025 08:24:28 +0000</pubDate>
      <link>https://dev.to/pydevtop/how-to-update-binance-p2p-ad-price-with-python-complete-step-by-step-tutorial-1p48</link>
      <guid>https://dev.to/pydevtop/how-to-update-binance-p2p-ad-price-with-python-complete-step-by-step-tutorial-1p48</guid>
      <description>&lt;p&gt;Managing cryptocurrency trading on Binance can be time-consuming, especially for P2P merchants who need to constantly adjust ad prices to stay competitive. By using the Binance API with Python, you can automatically update your ad prices in real time and reduce the need for manual adjustments.&lt;/p&gt;

&lt;p&gt;In this guide, you’ll learn:&lt;/p&gt;

&lt;p&gt;Why automating Binance P2P ad price updates is important&lt;/p&gt;

&lt;p&gt;What you need before getting started&lt;/p&gt;

&lt;p&gt;A working Python script for editing ad prices&lt;/p&gt;

&lt;p&gt;Common errors and how to fix them&lt;/p&gt;

&lt;p&gt;Why Automate Binance Ad Price Updates?&lt;/p&gt;

&lt;p&gt;Manual ad management on Binance P2P is inefficient and risky. Automating price changes provides several benefits:&lt;/p&gt;

&lt;p&gt;⚡ Instant reaction to market fluctuations&lt;br&gt;
📉 Lower risk of losses due to delays&lt;br&gt;
💼 Optimized trading workflows&lt;br&gt;
⏰ Significant time savings for merchants&lt;/p&gt;

&lt;p&gt;Automation is particularly valuable if you’re running multiple ads or trading in highly volatile markets.&lt;/p&gt;

&lt;p&gt;Requirements Before You Start&lt;/p&gt;

&lt;p&gt;To programmatically change ad prices on Binance P2P, you’ll need:&lt;/p&gt;

&lt;p&gt;Binance API keys (make sure you generate them from your merchant account).&lt;/p&gt;

&lt;p&gt;Verified merchant account (only merchants can modify ads through the API).&lt;/p&gt;

&lt;p&gt;Python with Requests library installed:&lt;br&gt;
&lt;code&gt;pip install requests&lt;br&gt;
&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Python Code Example: Update Binance P2P Ad Price
&lt;/h3&gt;

&lt;p&gt;Here’s a practical example of how to change an ad price using Python and the Binance API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import time
import hmac, hashlib
import requests

class BinanceAPI:
    def __init__(self, api_key, api_secret):
        self.api_key = api_key
        self.api_secret = api_secret

    def getServerTime(self):
        return int(time.time() * 1000)

    def getSignature(self):
        query_string = f"timestamp={self.getServerTime()}"
        return hmac.new(
            self.api_secret.encode("utf-8"),
            query_string.encode("utf-8"),
            hashlib.sha256
        ).hexdigest()

    def getApiKey(self):
        return self.api_key

    def edit_ad(self, adv_num, new_price):
        payload = {'advNo': adv_num, 'price': new_price}
        params = {'timestamp': self.getServerTime(), 'signature': self.getSignature()}
        headers = {"Content-Type": "application/json", "X-MBX-APIKEY": self.getApiKey()}

        res = requests.post(
            'https://api.binance.com/sapi/v1/c2c/ads/update?',
            headers=headers, json=payload, params=params
        ).json()

        if res.get('code') == -1002:
            print("Access error: Merchant API keys required.")
        elif res.get('success'):
            print(f"Price updated: ad {adv_num} → new price {new_price}")
        else:
            print(f"Update error: {res.get('message', 'Unknown error')}")

        return res

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

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if __name__ == '__main__':
    api = BinanceAPI("YOUR_API_KEY", "YOUR_API_SECRET")
    api.edit_ad("1234567890987654321", "46")

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

&lt;/div&gt;



&lt;p&gt;Common Errors and Solutions&lt;/p&gt;

&lt;p&gt;Error code -1002: Access denied → Use merchant API keys.&lt;/p&gt;

&lt;p&gt;success: False: Incorrect ad ID → Verify ad number.&lt;/p&gt;

&lt;p&gt;HTTP 401 or 403: Authorization issue → Double-check API key and secret.&lt;/p&gt;

&lt;p&gt;Ready-Made Binance P2P Bot&lt;/p&gt;

&lt;p&gt;If you don’t want to build everything from scratch, consider using a ready-to-use &lt;a href="https://py-dev.top/application-software/p2p-trade-bot-binance"&gt;Binance P2P Trade Bot&lt;/a&gt;.&lt;br&gt;
This bot automatically:&lt;/p&gt;

&lt;p&gt;Monitors competitor prices&lt;/p&gt;

&lt;p&gt;Adjusts your ads to stay competitive&lt;/p&gt;

&lt;p&gt;Saves you time and increases efficiency&lt;/p&gt;

&lt;p&gt;Final Thoughts&lt;/p&gt;

&lt;p&gt;Automating Binance P2P ad price changes with Python is an essential strategy for serious crypto traders. It helps you:&lt;/p&gt;

&lt;p&gt;Stay competitive in fast-moving markets&lt;/p&gt;

&lt;p&gt;Reduce manual effort&lt;/p&gt;

&lt;p&gt;Improve trading efficiency&lt;/p&gt;

&lt;p&gt;Whether you’re creating your own Binance trading bot or just optimizing a few ads, Python + Binance API gives you full control over your P2P pricing strategy.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
