7 min read

How to Implement the Engulfing Candle Strategy on Coinbase with Python

Learn how to combine indicator detection into providing you with trading signals using Python 3 and the Coinbase Pro REST API.
Depicts a robotic hand holding a stylized Bitcoin, shaped like a coin. Title image for the series Coinbase Crypto Trading Bot
Title Image for Series Coinbase Crypto Trading Bot

Coinbase Crypto Trading Bot

Welcome!

I’ve been writing a series on how to use Python to analyze Coinbase data. In previous episodes, I’ve demonstrated how to:

  • Set up a project to securely import your Coinbase keys
  • Retrieve data from Coinbase
  • Detect Bearish Engulfing Patterns and Bullish Engulfing Patterns

In this episode, I’ll show you how to integrate these detections into a cohesive trading strategy.


About the Series

In this series, I show you how to build your own crypto trading bot.

The series uses Python 3 to connect to Coinbase, then implements a modified version of the well-known Engulfing Candle Trading Strategy. Throughout the project, I show you how to set up your project in a way that makes it easy to expand with your own ideas and strategies in the future.

All code for the series can be found on the project GitHub. Each episode has working code samples you can copy and paste straight into your own crypto trading bot.

By The End of This Episode

You’ll be generating trading signals based on the Engulfing Pattern!


From Indicator To Automated Trading Strategy

Strategy Construction

A strategy is a combination of two factors:

  • An indicator or indicators which identify a predicted price movement
  • A predicted trade signal that allows you to maximize the profit from a price movement and minimize the risk if the prediction is incorrect

There are thousands (maybe millions) of different combinations of indicators and predicted trade signals. As you develop expertise in markets and quantitative analysis, you’ll probably develop your own.

Breaking this down into high-level requirements, we could state that our strategy must:

  1. Automatically detect specified indicator(s)
  2. Automatically generate trade signals with their associated Take Profit, Stop Loss, and Entry prices

The Engulfing Candle Strategy

In this series, we’ll be implementing the Engulfing Candle Strategy. This is a well-known, well-documented strategy, which states the following:

  1. If a bullish engulfing pattern is detected, create a trade signal with the following values: Buy Stop = Green Candle High, Stop Loss = Green Candle Low, Take Profit = 1:1 Win
  2. If a bearish engulfing pattern is detected, create a trade signal with the following values: Sell Stop = Red Candle Low, Stop Loss = Red Candle High, Take Profit = 1:1 Win
  3. The strategy is most effective at higher timeframes (i.e. Daily)

The series has already covered how to detect our modified bullish engulfing pattern and bearish engulfing pattern, so now we can focus on converting our indicators into signals.

Let’s Code

Let’s convert our requirements and strategy into code.

Create engulfing_candle_strategy.py

I’ll start by creating a file called strategy_one.py in the folder strategies. This file will respond to detected engulfing patterns and output the trade signal.

In the file, add the following code:

# Function to respond to engulfing candle detections and turn them into a strategy
def engulfing_candle_strategy(high, low, symbol, timeframe, exchange, alert_type, project_settings):
    """
    Function to respond to engulfing candle detections and turn them into a strategy
    :param high: float
    :param low: float
    :param symbol: string
    :param timeframe: string
    :param exchange: string
    :param alert_type: string
    :param project_settings: json dictionary object
    :return:
    """
    # Only apply strategy to specified timeframes
    if timeframe == "M15" or timeframe == "M30" or timeframe == "H1" or timeframe == "D1":
        # Respond to bullish_engulfing
        if alert_type == "bullish_engulfing":
            # Set the Trade Type
            trade_type = "BUY"
            # Set the Take Profit
            take_profit = high + high - low
            # Set the Buy Stop
            entry_price = high
            # Set the Stop Loss
            stop_loss = low
        elif alert_type == "bearish_engulfing":
            # Set the Trade Type
            trade_type = "SELL"
            # Set the Take Profit
            take_profit = low - high + low
            # Set the Sell Stop
            entry_price = low
            # Set the Stop Loss
            stop_loss = high
        # Print the result to the screen
        print(f"Trade Signal Detected. Symbol: {symbol}, Trade Type: {trade_type}, Take Profit: {take_profit}, "
              f"Entry Price: {entry_price}, Stop Loss: {stop_loss}, Exchange: {exchange}")

P.S. if you’re wondering about the exchange variable in this function, this is to make our code as extensible as possible. This will allow us to use different exchanges in the future, each of which has its own methods of interacting.

Update the Bullish Engulfing Detection

Let’s return to the bullish_engulfing function from episode 3. The function needs to be updated to pass its results to engulfing_candle_strategy. Update it as follows:

  • Add in the import statement from strategies import engulfing_candle_strategy
  • Add two variables into the function declaration: exchange and project_settings
  • Above the line return True add this code block:
strategy = engulfing_candle_strategy.engulfing_candle_strategy(
                            high=most_recent_candle['high'],
                            low=most_recent_candle['low'],
                            symbol=most_recent_candle['symbol'],
                            timeframe=most_recent_candle['timeframe'],
                            exchange=exchange,
                            alert_type="bullish_engulfing",
                            project_settings=project_settings
                        )

The full code for bullish_engulfing.py should now look like this:

from indicators import ema_calculator
from strategies import engulfing_candle_strategy


# Function to calculate bullish engulfing pattern
def calc_bullish_engulfing(dataframe, exchange, project_settings):
    """
    Function to calculate if a bullish engulfing candle has been detected
    :param dataframe: Pandas dataframe of candle data
    :param exchange: string
    :param project_settings: JSON data object
    :return: Bool
    """
    # Extract the most recent candle
    len_most_recent = len(dataframe) - 1
    most_recent_candle = dataframe.loc[len_most_recent]

    # Extract the second most recent candle
    len_second_most_recent = len(dataframe) - 2
    second_most_recent_candle = dataframe.loc[len_second_most_recent]

    # Calculate if second most recent candle Red
    if second_most_recent_candle['close'] < second_most_recent_candle['open']:
        # Calculate if most recent candle green
        if most_recent_candle['close'] > most_recent_candle['open']:
            # Check the Green Body > Red Body
            # Red Body
            red_body = second_most_recent_candle['open'] - second_most_recent_candle['close']
            # Green Body
            green_body = most_recent_candle['close'] - second_most_recent_candle['open']
            # Compare
            if green_body > red_body:
                # Compare Green High > Red High
                if most_recent_candle['high'] > second_most_recent_candle['high']:
                    # Calculate the 20-EMA
                    ema_20 = ema_calculator.calc_ema(dataframe=dataframe, ema_size=20)
                    # Extract the second most recent candle from the new dataframe
                    ema_count = len(ema_20) - 2
                    ema_second_most_recent = ema_20.loc[ema_count]
                    # Compare the EMA and Red Low
                    if ema_second_most_recent['close'] < ema_second_most_recent['ema_20']:
                        strategy = engulfing_candle_strategy.engulfing_candle_strategy(
                            high=most_recent_candle['high'],
                            low=most_recent_candle['low'],
                            symbol=most_recent_candle['symbol'],
                            timeframe=most_recent_candle['timeframe'],
                            exchange=exchange,
                            alert_type="bullish_engulfing",
                            project_settings=project_settings
                        )
                        return True
    return False

Update the Bearish Engulfing Detection

The bearish_engulfing function also needs updating. Update it as follows:

  • Add in the import statement from strategies import engulfing_candle_strategy
  • Add two variables into the function declaration: exchange and project_settings
  • Above the line return True add this code block:
strategy = engulfing_candle_strategy.engulfing_candle_strategy(
                            high=most_recent_candle['high'],
                            low=most_recent_candle['low'],
                            symbol=most_recent_candle['symbol'],
                            timeframe=most_recent_candle['timeframe'],
                            exchange=exchange,
                            alert_type="bearish_engulfing",
                            project_settings=project_settings
                        )

The full code for bearish_engulfing.py should now look like this:

from indicators import ema_calculator
from strategies import engulfing_candle_strategy


# Function to calculate bearish engulfing pattern
def calc_bearish_engulfing(dataframe, exchange, project_settings):
    """
    Function to detect a bearish engulfing pattern
    :param dataframe: Pandas dataframe of candle data
    :param exchange: string
    :param project_settings: JSON data object
    :return: Bool
    """

    # Extract the most recent candle
    len_most_recent = len(dataframe) - 1
    most_recent_candle = dataframe.loc[len_most_recent]

    # Extract the second most recent candle
    len_second_most_recent = len(dataframe) - 2
    second_most_recent_candle = dataframe.loc[len_second_most_recent]

    # Calculate if the second most recent candle is Green
    if second_most_recent_candle['close'] > second_most_recent_candle['open']:
        # Calculate if most recent candle is Red
        if most_recent_candle['close'] < most_recent_candle['open']:
            # Check the Red Body > Red Body
            # Red Body
            red_body = most_recent_candle['open'] - most_recent_candle['close']
            # Green Body
            green_body = second_most_recent_candle['close'] - second_most_recent_candle['open']
            # Compare
            if red_body > green_body:
                # Compare Red low and Green low
                if most_recent_candle['low'] < second_most_recent_candle['low']:
                    # Calculate the 20-EMA
                    ema_20 = ema_calculator.calc_ema(dataframe=dataframe, ema_size=20)
                    # Extract the second most recent candle from the new dataframe
                    ema_count = len(ema_20) - 2
                    ema_second_most_recent = ema_20.loc[ema_count]
                    # Compare 20-EMA and Green Open
                    if ema_second_most_recent['open'] > ema_second_most_recent['ema_20']:
                        strategy = engulfing_candle_strategy.engulfing_candle_strategy(
                            high=most_recent_candle['high'],
                            low=most_recent_candle['low'],
                            symbol=most_recent_candle['symbol'],
                            timeframe=most_recent_candle['timeframe'],
                            exchange=exchange,
                            alert_type="bearish_engulfing",
                            project_settings=project_settings
                        )
                        return True
    return False

Update main.py

Finally, let’s update main.py to include the two extra variables we added to each function.

Here’s what it should look like (only update __main__ ):

# Press the green button in the gutter to run the script.
if __name__ == '__main__':
    # Import project settings
    project_settings = get_project_settings(import_filepath)
    # Retrieve the account details
    # account_details = get_account_details.get_account_details(project_settings=project_settings)
    candlestick_data = get_candlesticks.get_candlestick_data("BTC-USDC", "D1")
    # Check Bullish Engulfing
    bullish_engulfing_check = bullish_engulfing.calc_bullish_engulfing(
        dataframe=candlestick_data,
        exchange="coinbase",
        project_settings=project_settings
    )
    print(f"Bullish Engulfing Check: {bullish_engulfing_check}")
    bearish_engulfing_check = bearish_engulfing.calc_bearish_engulfing(
        dataframe=candlestick_data,
        exchange="coinbase",
        project_settings=project_settings
    )
    print(f"Bearish Engulfing Check: {bearish_engulfing_check}")
  

If you press play now, you may see something cool happen. If an engulfing pattern has occurred, you should see your screen update with some recommended trade values!

In Conclusion

Many traders would be happy with the code developed so far. We can successfully retrieve historic price data and analyze it according to our strategy.

Where’s the STOP LOSS and TAKE PROFIT?

More advanced traders will now want to automate the placing and canceling of trades. Unfortunately for us, Coinbase Pro (the retail edition) doesn’t offer some of the features we need for this, including the addition of a STOP LOSS, TAKE PROFIT, or TRAILING STOP.

I’ll keep an eye on the Coinbase Pro API for when these become available.

Ongoing Monitoring

Followers of my other tutorials will know that I typically include methods to detect when new candles arrive. This allows you to set your trading bot into ‘Auto-Mode’.

Unfortunately, Coinbase specifically calls out not using their API to poll for new candles. Therefore, in order to monitor for new candles, we would need to implement a socket.

I’ve made the decision, that along with the lack of STOP LOSS and TAKE PROFIT deficiencies, a separate series will be needed to cover all the items.

Say Hi!

I love hearing from my readers, so feel free to reach out. It means a ton to me when you clap for my articles or drop a friendly comment — it helps me know that my content is helping.