<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged algotading</title>
<link>https://coderlegion.com/tag/algotading</link>
<description>Powered by Question2Answer</description>
<item>
<title>How to Add Real-Time Orderflow Intelligence to Your Freqtrade Bot in 10 Lines of Python</title>
<link>https://coderlegion.com/15796/how-to-add-real-time-orderflow-intelligence-to-your-freqtrade-bot-in-10-lines-of-python</link>
<description>&lt;h2 id=&quot;how-to-add-real-time-orderflow-intelligence-to-your-freqtrade-bot-in-10-lines-of-python&quot;&gt;How to Add Real-Time Orderflow Intelligence to Your Freqtrade Bot in 10 Lines of Python&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Stop letting RSI lie to your bot. Here's how to give it actual market intelligence.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-problem-every-freqtrade-user-knows&quot;&gt;The Problem Every Freqtrade User Knows&lt;/h3&gt;
&lt;p&gt;You've tuned your strategy. The backtests look great. You go live — and your bot walks straight into a trap.&lt;/p&gt;
&lt;p&gt;The RSI said oversold. The EMA said buy. Everything looked perfect.&lt;/p&gt;
&lt;p&gt;But something in the orderbook was screaming danger — and your bot had no way to hear it.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;what-actually-happened&quot;&gt;What Actually Happened&lt;/h3&gt;
&lt;p&gt;I ran a live experiment: I fed the same market data to both traditional indicators and a real-time L2 orderbook analyzer simultaneously, then recorded what each one saw.&lt;/p&gt;
&lt;p&gt;Here's the exact 60-second timeline on BTCUSDT:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Time &lt;/th&gt;&lt;th&gt;Orderflow Signal &lt;/th&gt;&lt;th&gt;RSI &lt;/th&gt;&lt;th&gt;What Was Happening&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;22:03:40 &lt;/td&gt;&lt;td&gt;&lt;code&gt;BUY_PRESSURE&lt;/code&gt; / delta_accel: &lt;strong&gt;0.4&lt;/strong&gt; &lt;/td&gt;&lt;td&gt;43 &lt;/td&gt;&lt;td&gt;Institutional buying detected&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;22:03:49 &lt;/td&gt;&lt;td&gt;&lt;code&gt;SELL_PRESSURE&lt;/code&gt; / delta_accel: &lt;strong&gt;10.4&lt;/strong&gt; &lt;/td&gt;&lt;td&gt;43 &lt;/td&gt;&lt;td&gt;&lt;strong&gt;26x selling spike — RSI silent&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;22:03:57 &lt;/td&gt;&lt;td&gt;&lt;code&gt;NEUTRAL&lt;/code&gt; / delta_accel: 2.37 &lt;/td&gt;&lt;td&gt;43 &lt;/td&gt;&lt;td&gt;Absorption phase begins&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;22:04:15 &lt;/td&gt;&lt;td&gt;&lt;code&gt;BUY_PRESSURE&lt;/code&gt; / delta_accel: 0.05 &lt;/td&gt;&lt;td&gt;43 &lt;/td&gt;&lt;td&gt;Buyers returning&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;RSI stayed at 43 the entire time.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;While a complete institutional selling cycle — spike, absorption, recovery — happened in the orderbook, the indicator that millions of bots rely on did not move a single point.&lt;/p&gt;
&lt;p&gt;This is not a bug. This is how lagging indicators work by design.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-fix-pre-entry-orderflow-confirmation&quot;&gt;The Fix: Pre-Entry Orderflow Confirmation&lt;/h3&gt;
&lt;p&gt;The solution isn't to replace your strategy. It's to add one layer of reality-checking before any order is submitted.&lt;/p&gt;
&lt;p&gt;Freqtrade has a perfect hook for this: &lt;code&gt;confirm_trade_entry()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This function is called exactly once, right before Freqtrade submits a buy order. If it returns &lt;code&gt;False&lt;/code&gt;, the trade is blocked. If it returns &lt;code&gt;True&lt;/code&gt;, execution proceeds.&lt;/p&gt;
&lt;p&gt;Here's the complete integration:&lt;/p&gt;
&lt;h4 id=&quot;step-1-install&quot;&gt;Step 1: Install&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;pip install horus-flow-mcp requests
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Get your free API key at &lt;a rel=&quot;nofollow&quot; href=&quot;https://rapidapi.com/horustechltd/api/horus-flow-intelligence&quot;&gt;RapidAPI — Horus Flow Intelligence&lt;/a&gt;&lt;/p&gt;
&lt;h4 id=&quot;step-2-the-connector-save-as-horus-connector-py-&quot;&gt;Step 2: The Connector (save as &lt;code&gt;horus_connector.py&lt;/code&gt;)&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import requests
import logging
from typing import Optional

logger = logging.getLogger(&quot;HorusConnector&quot;)

class HorusConnector:
    &quot;&quot;&quot;
    Pre-trade orderflow filter for Freqtrade.
    Blocks entries when institutional orderflow is adversarial.
    Fail-OPEN: if API is unreachable, trade proceeds normally.
    &quot;&quot;&quot;

    DANGER_SIGNALS = {
        &quot;SELL_PRESSURE&quot;,
        &quot;LIQUIDITY_EVENT&quot;,
        &quot;EMERGENCY_DUMP&quot;,
        &quot;BAILOUT&quot;
    }

    ENTRY_SIGNALS = {
        &quot;BUY_PRESSURE&quot;,
        &quot;BUY_ABSORPTION&quot;   # Strongest signal: whales absorbing sell pressure
    }

    def __init__(self, api_key: str, min_confidence: float = 0.70):
        self.min_confidence = min_confidence
        self.headers = {
            &quot;x-rapidapi-key&quot;: api_key,
            &quot;x-rapidapi-host&quot;: &quot;horus-flow-intelligence.p.rapidapi.com&quot;
        }
        self.base_url = &quot;https://horus-flow-intelligence.p.rapidapi.com&quot;

    def is_safe_to_enter(self, symbol: str) -&amp;gt; bool:
        &quot;&quot;&quot;
        Returns True only when orderflow confirms favorable conditions.
        Returns True on API failure (fail-open = don't block on network issues).
        &quot;&quot;&quot;
        symbol = symbol.replace(&quot;/&quot;, &quot;&quot;).upper()

        try:
            response = requests.get(
                f&quot;{self.base_url}/v1/flow/crypto/{symbol}&quot;,
                headers=self.headers,
                timeout=2
            )
            data = response.json()

            signal = data.get(&quot;signal&quot;, &quot;NEUTRAL&quot;)
            confidence = data.get(&quot;confidence&quot;, 0)
            flags = data.get(&quot;metrics&quot;, {}).get(&quot;flags&quot;, [])

            # Hard block: dangerous market conditions
            if signal in self.DANGER_SIGNALS:
                logger.info(f&quot;[Horus ] {symbol} blocked — {signal} ({confidence:.0%})&quot;)
                return False

            # Hard block: spoofing detected regardless of signal
            if any(&quot;SPOOFING_DETECTED&quot; in f for f in flags):
                logger.info(f&quot;[Horus ] {symbol} blocked — Spoofing: {flags}&quot;)
                return False

            # Green light: confirmed institutional buy-side activity
            if signal in self.ENTRY_SIGNALS and confidence &amp;gt;= self.min_confidence:
                logger.info(f&quot;[Horus ✅] {symbol} confirmed — {signal} ({confidence:.0%})&quot;)
                return True

            # Gray zone: neutral or low conviction
            logger.info(f&quot;[Horus ⏳] {symbol} waiting — {signal} ({confidence:.0%})&quot;)
            return False

        except Exception:
            # Network issue: fail-open, let Freqtrade decide
            return True
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;step-3-add-to-your-strategy-10-lines-&quot;&gt;Step 3: Add to Your Strategy (10 lines)&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import os
from horus_connector import HorusConnector
from freqtrade.strategy import IStrategy
from pandas import DataFrame

class HorusFilteredStrategy(IStrategy):
    &quot;&quot;&quot;
    Any existing strategy + Horus orderflow pre-entry confirmation.
    Replace populate_indicators/entry/exit with your own logic.
    &quot;&quot;&quot;

    timeframe = &quot;5m&quot;
    minimal_roi = {&quot;0&quot;: 0.03}
    stoploss = -0.02

    # Initialize Horus — reads key from environment variable
    horus = HorusConnector(
        api_key=os.getenv(&quot;RAPIDAPI_KEY&quot;, &quot;&quot;),
        min_confidence=0.70
    )

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -&amp;gt; DataFrame:
        # Your existing indicator logic here
        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -&amp;gt; DataFrame:
        # Your existing entry conditions here
        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -&amp;gt; DataFrame:
        # Your existing exit conditions here
        return dataframe

    def confirm_trade_entry(self, pair: str, **kwargs) -&amp;gt; bool:
        &quot;&quot;&quot;
        ← This is the only addition to your existing strategy.
        Horus checks the live orderbook before every single entry.
        If orderflow is adversarial, the trade is blocked.
        &quot;&quot;&quot;
        return self.horus.is_safe_to_enter(pair)
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;step-4-run&quot;&gt;Step 4: Run&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;export RAPIDAPI_KEY=&quot;your_key_here&quot;
freqtrade trade --strategy HorusFilteredStrategy --dry-run
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&quot;what-this-does-in-practice&quot;&gt;What This Does in Practice&lt;/h3&gt;
&lt;p&gt;Your bot's existing logic still generates entry signals. Nothing changes there.&lt;/p&gt;
&lt;p&gt;The only difference: before Freqtrade submits the order, it now asks one additional question — &lt;em&gt;&quot;What is the live orderbook actually saying right now?&quot;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;If the answer is &lt;code&gt;SELL_PRESSURE&lt;/code&gt;, &lt;code&gt;LIQUIDITY_EVENT&lt;/code&gt;, or &lt;code&gt;SPOOFING_DETECTED&lt;/code&gt;, the trade is blocked.&lt;br&gt;
If the answer is &lt;code&gt;BUY_PRESSURE&lt;/code&gt; or &lt;code&gt;BUY_ABSORPTION&lt;/code&gt; with sufficient confidence, execution proceeds.&lt;br&gt;
If the answer is &lt;code&gt;NEUTRAL&lt;/code&gt; or low confidence, the system waits.&lt;/p&gt;
&lt;p&gt;Here's what the logs look like:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;[Horus ✅] ETHUSDT confirmed — BUY_ABSORPTION (90%)
[Horus ] BTCUSDT blocked — SELL_PRESSURE (85%)
[Horus ] SOLUSDT blocked — Spoofing detected
[Horus ⏳] ADAUSDT waiting — NEUTRAL (60%)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&quot;what-selective-filtering-looks-like-in-practice&quot;&gt;What Selective Filtering Looks Like in Practice&lt;/h3&gt;
&lt;p&gt;In a typical live session, the system scans thousands of assets continuously. Of all the technical signals generated, only a small fraction pass the orderflow confirmation layer.&lt;/p&gt;
&lt;p&gt;This low pass-rate is not a bug — it's the filter working correctly.&lt;/p&gt;
&lt;p&gt;Most market conditions are not suitable for entry. A bot that knows when &lt;strong&gt;not&lt;/strong&gt; to trade is more valuable than one that trades constantly. Every blocked entry is a potential loss avoided.&lt;/p&gt;
&lt;p&gt;The logs tell the story:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;[Horus ✅] ETHUSDT confirmed — BUY_ABSORPTION (90%)  → trade executed
[Horus ] BTCUSDT blocked — SELL_PRESSURE (85%)    → entry skipped
[Horus ] SOLUSDT blocked — LIQUIDITY_EVENT (99%)  → entry skipped
[Horus ⏳] ADAUSDT waiting — NEUTRAL (60%)           → no conviction
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The ratio of blocked to confirmed entries will vary by market condition. In volatile or manipulated markets, the filter becomes more selective. In clean trending conditions, more signals pass through.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;understanding-the-key-signals&quot;&gt;Understanding the Key Signals&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Signal &lt;/th&gt;&lt;th&gt;What It Means &lt;/th&gt;&lt;th&gt;Action&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;BUY_PRESSURE&lt;/code&gt; &lt;/td&gt;&lt;td&gt;Institutional aggression on ask side &lt;/td&gt;&lt;td&gt;✅ Allow entry&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;BUY_ABSORPTION&lt;/code&gt; &lt;/td&gt;&lt;td&gt;Whales absorbing sell pressure — strongest reversal signal &lt;/td&gt;&lt;td&gt;✅ Allow entry&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;SELL_PRESSURE&lt;/code&gt; &lt;/td&gt;&lt;td&gt;Institutional selling dominant &lt;/td&gt;&lt;td&gt; Block entry&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;LIQUIDITY_EVENT&lt;/code&gt; &lt;/td&gt;&lt;td&gt;Flash crash setup detected &lt;/td&gt;&lt;td&gt; Block entry&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;EMERGENCY_DUMP&lt;/code&gt; &lt;/td&gt;&lt;td&gt;Aggressive institutional exit &lt;/td&gt;&lt;td&gt; Block entry&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;SPOOFING_DETECTED&lt;/code&gt; &lt;/td&gt;&lt;td&gt;Fake bid/ask walls placed to mislead &lt;/td&gt;&lt;td&gt; Block entry&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;NEUTRAL&lt;/code&gt; &lt;/td&gt;&lt;td&gt;No clear institutional intent &lt;/td&gt;&lt;td&gt;⏳ Wait&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h3 id=&quot;what-buy-absorption-actually-looks-like&quot;&gt;What BUY_ABSORPTION Actually Looks Like&lt;/h3&gt;
&lt;p&gt;This is the signal worth understanding in depth. During a period when BTC and SOL were both showing &lt;code&gt;SELL_PRESSURE&lt;/code&gt;, ETH showed this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;symbol&quot;: &quot;ETHUSDT&quot;,
  &quot;signal&quot;: &quot;BUY_ABSORPTION&quot;,
  &quot;confidence&quot;: 0.80,
  &quot;market_state&quot;: &quot;COUNTER_TREND&quot;,
  &quot;metrics&quot;: {
    &quot;bid_ask_ratio&quot;: 21.32,
    &quot;buy_sell_ratio&quot;: 0.78,
    &quot;whale_activity&quot;: true
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;bid_ask_ratio: 21.32&lt;/code&gt; means there's 21x more bid liquidity than ask. Someone is systematically absorbing every sell order. RSI at this moment was in the low 40s — suggesting bearish conditions.&lt;/p&gt;
&lt;p&gt;A bot running on indicators alone would see no reason to buy.&lt;br&gt;
A bot reading the orderbook would see institutions quietly loading.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;tuning-the-filter&quot;&gt;Tuning the Filter&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;min_confidence&lt;/code&gt; parameter controls how selective the filter is:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;# Conservative — only high-conviction signals pass
horus = HorusConnector(api_key=key, min_confidence=0.80)

# Balanced — good for most strategies
horus = HorusConnector(api_key=key, min_confidence=0.70)

# Permissive — catches more opportunities, more noise
horus = HorusConnector(api_key=key, min_confidence=0.60)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Start at &lt;code&gt;0.70&lt;/code&gt; and adjust based on your strategy's characteristics.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;backtest-safety&quot;&gt;Backtest Safety&lt;/h3&gt;
&lt;p&gt;One important note: &lt;code&gt;confirm_trade_entry()&lt;/code&gt; is only called in live and dry-run modes. During backtesting, Freqtrade skips it entirely — so your backtests won't be affected, and you won't get errors from missing API calls.&lt;/p&gt;
&lt;p&gt;This is by design. Orderflow data from the past doesn't exist in the way historical OHLCV data does. The filter is a live tool only.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;getting-started&quot;&gt;Getting Started&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# Install
pip install horus-flow-mcp requests

# Get free API key (500 calls/month on free tier)
# https://rapidapi.com/horustechltd/api/horus-flow-intelligence

# Clone the strategy template
git clone https://github.com/horustechltd/freqtrade-horus-strategy

# Run dry
export RAPIDAPI_KEY=&quot;your_key&quot;
freqtrade trade --strategy HorusFlowStrategy --dry-run
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Free tier:&lt;/strong&gt; 500 API calls/month — enough for testing and low-frequency strategies.&lt;br&gt;
&lt;strong&gt;Pro tier ($29/month):&lt;/strong&gt; 100,000 calls — suitable for active bots across multiple pairs.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;what-s-next&quot;&gt;What's Next&lt;/h3&gt;
&lt;p&gt;If you want to go deeper:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Emergency exit protection:&lt;/strong&gt; Use &lt;code&gt;custom_exit()&lt;/code&gt; to close positions when &lt;code&gt;LIQUIDITY_EVENT&lt;/code&gt; is detected mid-trade&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;US Equities:&lt;/strong&gt; The same API works for stocks — &lt;code&gt;get_equity_flow(&quot;NVDA&quot;)&lt;/code&gt; returns institutional orderflow for US markets&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Multi-pair scanner:&lt;/strong&gt; &lt;code&gt;scan_crypto_flow()&lt;/code&gt; checks multiple symbols in one call — useful for portfolio-level decisions&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The full strategy template with emergency exit logic is available at:&lt;br&gt;
&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/horustechltd/freqtrade-horus-strategy&quot;&gt;github.com/horustechltd/freqtrade-horus-strategy&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;All trade data and orderflow readings in this article come from live production systems. Past performance does not guarantee future results. This is not financial advice.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;About the author:&lt;/strong&gt;&lt;br&gt;
Ashraf Hanafy is the founder of Horus Tech Ltd, building institutional-grade market microstructure intelligence for AI trading agents.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/horustechltd&quot;&gt;horustechltd&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;MCP Server: &lt;a rel=&quot;nofollow&quot; href=&quot;https://pypi.org/project/horus-flow-mcp/&quot;&gt;pip install horus-flow-mcp&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;LinkedIn: &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.linkedin.com/in/ashraf-hanafy-b11ba440&quot;&gt;Ashraf Hanafy&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/15796/how-to-add-real-time-orderflow-intelligence-to-your-freqtrade-bot-in-10-lines-of-python</guid>
<pubDate>Sat, 25 Apr 2026 06:59:58 +0000</pubDate>
</item>
</channel>
</rss>