Close Navigation
Learn more about IBKR accounts

Implementing Donchian Channel Trading App

Lesson 3 of 4

Duration 9:23
Level Beginner
Close Navigation

Contributed By: PyQuant News

Building our trading application

The `TradingApp` class is a Python application that interacts with the Interactive Brokers (IB) API. It extends `EClient` and `EWrapper` classes to manage connections, handle data, and execute trades. Below is a concise explanation of its key components:

Class Overview

Parent Classes**: Inherits from `EClient` and `EWrapper`, enabling communication and interaction with the IB API.

Attributes:

– `data`: A dictionary storing historical data fetched from IB, indexed by request IDs.

– `nextOrderId`: An integer storing the next valid order ID needed to place trades.

import time
import threading
from datetime import datetime
from typing import Dict, Optional
import pandas as pd
import warnings
warnings.filterwarnings("ignore")
from ibapi.client import EClient
from ibapi.wrapper import EWrapper
from ibapi.contract import Contract
from ibapi.order import Order
from ibapi.common import BarData

Donchian Channel Overview

The Donchian Channel is a trend-following indicator widely used by traders to identify market trends, breakouts, and trading opportunities. It consists of three key lines:

Upper Band: The highest high over a specified period.

Lower Band: The lowest low over the same period.

Middle Line: The average of the upper and lower bands.

How Traders Use Donchian Channels

1. Identifying Breakouts

– Traders use the upper and lower bands to spot breakouts. A price crossing above the upper band signals a bullish breakout, indicating a potential buy opportunity. Conversely, a price crossing below the lower band suggests a bearish breakout, signaling a potential sell opportunity.

2. Trend Confirmation

– The Donchian Channel helps confirm existing trends. If the price stays near or above the upper band, it indicates strong upward momentum, suggesting the trend may continue. If the price stays near or below the lower band, it reflects strong downward momentum, guiding traders on whether to stay in or exit a trade.

3. Setting Entry and Exit Points

– Traders use the Donchian Channel to set entry and exit points. They might enter a long position when the price breaks above the upper band and exit when the price falls toward the middle or lower band. For short positions, traders enter when the price breaks below the lower band and exit as it rises back.

4. Risk Management

– The Donchian Channel is also used for risk management. Traders set stop-loss orders based on the lower band for long positions or the upper band for short positions, helping to limit potential losses if the market moves against their trade.

5. Range Trading

– During sideways markets, traders can trade within the Donchian Channel’s range. They buy near the lower band and sell near the upper band, profiting from price reversals within the channel.

The Donchian Channel is a versatile tool that helps traders identify breakouts, confirm trends, and improve their trading strategies. By analyzing price movements relative to the channel’s bands, traders can make better-informed decisions on entry, exit, and risk management, enhancing their overall trading effectiveness.

Building our trading application

The `TradingApp` class is a Python application that interacts with the Interactive Brokers (IB) API. It extends `EClient` and `EWrapper` classes to manage connections, handle data, and execute trades.

Class Overview

Parent Classes: Inherits from `EClient` and `EWrapper`, enabling communication and interaction with the IB API.

Attributes:

– `data`: A dictionary storing historical data fetched from IB, indexed by request IDs.

– `nextOrderId`: An integer storing the next valid order ID needed to place trades.

Key Methods

1. Initialization (`__init__`):

– Initializes the `TradingApp` class.

– Sets up the client and wrapper, and initializes the `data` dictionary to store historical data.

2. Error Handling (`error`):

– Handles errors received from IB API.

– Prints the error details including request ID, error code, and descriptive error message.

3. Order ID Management (`nextValidId`):

– Receives and sets the next valid order ID from IB.

– Updates the `nextOrderId` attribute to ensure correct order placement.

4. Fetching Historical Data (`get_historical_data`):

– Requests historical market data for a given contract.

– Stores the data in a Pandas DataFrame, indexed by time with columns for ‘high’, ‘low’, and ‘close’ prices.

– Returns the DataFrame containing the historical data.

5. Storing Historical Data (`historicalData`):

– Processes and stores historical data received from IB callbacks.

– Updates the DataFrame with high, low, and close prices based on the received data.

6. Creating Contract (`get_contract`):

– Creates a contract object for a stock based on the provided symbol.

– Configures the contract to trade on the SMART exchange in USD.

7. Placing Orders (`place_order`):

– Places an order using the provided contract, action (buy/sell), order type (e.g., market, limit), and quantity.

– Uses the `nextOrderId`, places the order, increments the order ID, and confirms the order placement.

The `TradingApp` class facilitates automated trading by directly interacting with the IB API. It manages error handling, data requests, and order executions programmatically, providing a framework for developing automated trading strategies.

class TradingApp(EClient, EWrapper):
    """
    A trading application class that interacts with the Interactive Brokers (IB) API.
    This class handles connections, data requests, and order placements using 
    the IB API by extending the EClient and EWrapper classes.

    Attributes
    ----------
    data : dict
        A dictionary to store historical data fetched from IB, with request IDs as keys.
    nextOrderId : int or None
        Stores the next valid order ID to be used for placing trades.

    Methods
    -------
    error(reqId, errorCode, errorString, advanced)
        Handles error messages received from IB.
    nextValidId(orderId)
        Receives and sets the next valid order ID for placing trades.
    get_historical_data(reqId, contract)
        Requests and retrieves historical market data for a given contract.
    historicalData(reqId, bar)
        Processes and stores historical data received from IB callbacks.
    get_contract(symbol)
        Creates and returns a stock contract object based on the provided symbol.
    place_order(contract, action, order_type, quantity)
        Places an order using the provided contract, action, order type, and quantity.
    """

    def __init__(self) -> None:
        """
        Initializes the TradingApp instance, setting up the client and wrapper 
        components, and initializing data storage.
        """
        EClient.__init__(self, self)
        self.data: Dict[int, pd.DataFrame] = {}
        self.nextOrderId: Optional[int] = None

    def error(self, reqId: int, errorCode: int, errorString: str, advanced: any) -> None:
        """
        Handles errors received from the IB API.

        Parameters
        ----------
        reqId : int
            The request ID associated with the error.
        errorCode : int
            The error code received from IB.
        errorString : str
            A descriptive error message.
        advanced : any
            Additional advanced error information (not commonly used).
        """
        print(f"Error: {reqId}, {errorCode}, {errorString}")
 def nextValidId(self, orderId: int) -> None:
        """
        Receives and sets the next valid order ID for placing trades.

        Parameters
        ----------
        orderId : int
            The next valid order ID received from IB.
        """
        super().nextValidId(orderId)
        self.nextOrderId = orderId

    def get_historical_data(self, reqId: int, contract: Contract) -> pd.DataFrame:
        """
        Requests and retrieves historical market data for a given contract.

        Parameters
        ----------
        reqId : int
            The request ID to associate with this historical data request.
        contract : ibapi.contract.Contract
            The contract object representing the financial instrument for which
            historical data is requested.

        Returns
        -------
        pandas.DataFrame
            A DataFrame containing the historical data, indexed by time with 
            columns for 'high', 'low', and 'close' prices.
        """
        self.data[reqId] = pd.DataFrame(columns=["time", "high", "low", "close"])
        self.data[reqId].set_index("time", inplace=True)
        self.reqHistoricalData(
            reqId=reqId,
            contract=contract,
            endDateTime="",
            durationStr="1 D",
            barSizeSetting="1 min",
            whatToShow="MIDPOINT",
            useRTH=0,
            formatDate=2,
            keepUpToDate=False,
            chartOptions=[],
        )
        time.sleep(5)
        return self.data[reqId]

  def historicalData(self, reqId: int, bar: BarData) -> None:
        """
        Processes and stores historical data received from IB callbacks.

        Parameters
        ----------
        reqId : int
            The request ID associated with this historical data.
        bar : ibapi.common.BarData
            The bar data received from IB containing high, low, and close prices.
        """
        # Get the current DataFrame at the request ID
        df = self.data[reqId]

        # Set the current bar data into the DataFrame
        df.loc[
            pd.to_datetime(bar.date, unit="s"), 
            ["high", "low", "close"]
        ] = [bar.high, bar.low, bar.close]

        # Cast data to floats
        df = df.astype(float)

        # Assign the DataFrame at the request ID
        self.data[reqId] = df

    @staticmethod
    def get_contract(symbol: str) -> Contract:
        """
        Creates and returns a stock contract object based on the provided symbol.

        Parameters
        ----------
        symbol : str
            The ticker symbol of the stock.

        Returns
        -------
        ibapi.contract.Contract
            A Contract object configured for the specified stock symbol.
        """
        contract = Contract()
        contract.symbol = symbol
        contract.secType = "STK"
        contract.exchange = "SMART"
        contract.currency = "USD"
        return contract

def place_order(self, contract: Contract, action: str, order_type: str, quantity: int) -> None:
        """
        Places an order using the provided contract, action, order type, and quantity.

        Parameters
        ----------
        contract : ibapi.contract.Contract
            The financial instrument to be traded.
        action : str
            The action to take ('BUY' or 'SELL').
        order_type : str
            The type of order ('MKT', 'LMT', etc.).
        quantity : int
            The number of shares/contracts to trade.
        """
        order = Order()
        order.action = action
        order.orderType = order_type
        order.totalQuantity = quantity

        self.placeOrder(self.nextOrderId, contract, order)
        self.nextOrderId += 1
        print("Buy order placed")

Areas to improve the app

1. Concurrency and Asynchronous Handling

Asynchronous Calls: Utilize Python’s `asyncio` or multi-threading to manage multiple data requests and order executions concurrently. This prevents the application from blocking during data fetches or order placements, enhancing overall responsiveness and performance.

Non-Blocking Operations: Replace `time.sleep()` with asynchronous waiting methods to avoid freezing the main thread, allowing other tasks to continue executing seamlessly.

2. Enhanced Error Handling and Logging

Robust Logging: Implement a logging framework (e.g., Python’s `logging` module) to record errors and events with timestamps and log levels (INFO, WARNING, ERROR). This approach provides better insights into the app’s behavior, aiding in troubleshooting and monitoring.

Error Recovery Mechanisms: Develop error recovery protocols, such as automatic retries for failed data requests or order placements, enhancing the app’s resilience in live trading environments.

3. Order Management and Risk Controls

Order Status Tracking: Extend order handling to include real-time tracking of order statuses (e.g., filled, partially filled, canceled) and integrate callbacks to update these statuses within the app.

Risk Management Features: Integrate basic risk management tools, such as setting maximum position sizes, stop-loss, and take-profit orders. This will help protect against significant losses and maintain disciplined trading practices.

These improvements will make the `TradingApp` more efficient, resilient, and safer to use in live trading scenarios, providing a solid foundation for further development and strategy implementation.

This is a third-party open-source library. It is not associated, supported, or managed by Interactive Brokers, completely independent. And if you’re using Pandas with your IB API or your trading apps, Interactive Brokers cannot help or offer support with Pandas specifically.

PyQuant News Site: https://www.pyquantnews.com/

Join The Conversation

For specific platform feedback and suggestions, please submit it directly to our team using these instructions.

If you have an account-specific question or concern, please reach out to Client Services.

We encourage you to look through our FAQs before posting. Your question may already be covered!

Leave a Reply

Disclosure: Interactive Brokers Third Party

Information posted on IBKR Campus that is provided by third-parties does NOT constitute a recommendation that you should contract for the services of that third party. Third-party participants who contribute to IBKR Campus are independent of Interactive Brokers and Interactive Brokers does not make any representations or warranties concerning the services offered, their past or future performance, or the accuracy of the information provided by the third party. Past performance is no guarantee of future results.

This material is from PyQuant News and is being posted with its permission. The views expressed in this material are solely those of the author and/or PyQuant News and Interactive Brokers is not endorsing or recommending any investment or trading discussed in the material. This material is not and should not be construed as an offer to buy or sell any security. It should not be construed as research or investment advice or a recommendation to buy, sell or hold any security or commodity. This material does not and is not intended to take into account the particular financial conditions, investment objectives or requirements of individual customers. Before acting on this material, you should consider whether it is suitable for your particular circumstances and, as necessary, seek professional advice.

Disclosure: API Examples Discussed

Throughout the lesson, please keep in mind that the examples discussed are purely for technical demonstration purposes, and do not constitute trading advice. Also, it is important to remember that placing trades in a paper account is recommended before any live trading.

IBKR Campus Newsletters

This website uses cookies to collect usage information in order to offer a better browsing experience. By browsing this site or by clicking on the "ACCEPT COOKIES" button you accept our Cookie Policy.