Detect candlestick patterns using TA-Lib and Python

As an Amazon Associate I earn from qualifying purchases.

4 Min Read

Candlestick patterns can be a great way of deciding if and when we should open or close a trade. For example, if we get a ‘hammer’ candlestick, this is usually a bullish sign. Paired with a moving average, this can be a powerful strategy. Let’s have a look at how we can detect candlestick patterns using TA-Lib and Python with the algorithmic trading bot.

For this tutorial, I will be using the hammer candlestick as an example. A full list of detectable candlestick patterns can be found here.

Adding candlestick patterns as constants

In TA-lib, candlestick patterns are functions that usually take the following form: CANDLESTICK(open, high, low, close). To reference these candlestick functions in our strategy (strategy.json), I found it best to add all the candlestick functions to a dictionary in constants.py using lambda expressions

Add the following code to constants.py:

cs_patterns = {
'hammer' : lambda openP, high, low, close: ta.CDLHAMMER(openP, high, low, close),
'closingMarubozu' : lambda openP, high, low, close:  ta.CDLCLOSINGMARUBOZU(openP, high, low, close),
'doji' : lambda openP, high, low, close: ta.CDLDOJI(openP, high, low, close),
'engulfing' : lambda openP, high, low, close:  ta.CDLENGULFING(openP, high, low, close),
'hangingMan' : lambda openP, high, low, close: ta.CDLHANGINGMAN(openP, high, low, close),
'hammer' : lambda openP, high, low, close: ta.CDLHAMMER(openP, high, low, close),
'invertedHammer' : lambda openP, high, low, close: ta.CDLINVERTEDHAMMER(openP, high, low, close),
'marubozu' : lambda openP, high, low, close: ta.CDLMARUBOZU(openP, high, low, close),
'beltHold': lambda openP, high, low, close: ta.CDLBELTHOLD(openP, high, low, close),
'breakaway' : lambda openP, high, low, close: ta.CDLBREAKAWAY(openP, high, low, close),
'inNeck' : lambda openP, high, low, close: ta.CDLINNECK(openP, high, low, close),
'kicking': lambda openP, high, low, close: ta.CDLKICKING(openP, high, low, close),
'kickingByLength' : lambda openP, high, low, close: ta.CDLKICKINGBYLENGTH(openP, high, low, close),
'ladderBottom' : lambda openP, high, low, close: ta.CDLLADDERBOTTOM(openP, high, low, close), 
'longLeggedDoji' : lambda openP, high, low, close: ta.CDLLONGLEGGEDDOJI(openP, high, low, close),
'longLineCdl' : lambda openP, high, low, close: ta.CDLLONGLINE(openP, high, low, close),
'shortLineCdl' : lambda openP, high, low, close: ta.CDLSHORTLINE(openP, high, low, close),
'rickShawMan' : lambda openP, high, low, close: ta.CDLRICKSHAWMAN(openP, high, low, close),
'spinningTop' : lambda openP, high, low, close: ta.CDLSPINNINGTOP(openP, high, low, close),
'stalled' : lambda openP, high, low, close: ta.CDLSTALLEDPATTERN(openP, high, low, close),
'stickSandwhich' : lambda openP, high, low, close: ta.CDLSTICKSANDWICH(openP, high, low, close),
'takuri' : lambda openP, high, low, close: ta.CDLTAKURI(openP, high, low, close),
'tasukiGap' : lambda openP, high, low, close: ta.CDLTASUKIGAP(openP, high, low, close),
'stalled' : lambda openP, high, low, close: ta.CDLSTALLEDPATTERN(openP, high, low, close),
'thrusting' : lambda openP, high, low, close: ta.CDLTHRUSTING(openP, high, low, close),
}

Using this dictionary, you will now be able to reference and candlestick pattern function by it’s key.

Adding a candlestick pattern to our strategy

In the strategy file, you will want to specify the candlestick pattern key. Add a new key under the movingAverages key and name it candlestickPattern. Set the value of this key to hammer:

{
  "accountNumber" : 40461345,
  "account_currency" : "USD",
	"strategy_name": "myStrategy",
	"pairs": [
		"EURUSD",
		"USDCAD",
		"GBPUSD"
    ],
    "risk" : 2,
    "maxLosses" : 3,
    "takeProfit": 700.0,
    "stopLoss": 300.0,
	  "movingAverages": {
        "SMA": {
            "val": 10,
            "aboveBelow": "below"
        },
        "EMA": {
            "val": 50,
            "aboveBelow": "above"
        }
    },
    "candlestickPattern" : "hammer",
    "maximumDrawdown" : 0.5,
    "initialBalance" : 100000
}

Detecting the candlestick pattern during live trading

The last step is to calculate the current candlestick for the current period and detect if it is the candlestick you are interested in trading. To do this, let’s go back to the check_trades method in trader.py. The check_trades method should look similar to the following:

def check_trades(time_frame, pair_data, strategy):
    moving_averages = strategy['movingAverages']
    for pair, data in pair_data.items():
        for m in moving_averages:
            ma_func = constants.movingAveragesFunctions[m]
            val = moving_averages [m]['val']
            data[m] = ma_func(data['close'], val)
            
        last_row = data.tail(1)
        open_positions = positions_get()
        current_dt = datetime.now().astimezone(pytz.timezone('Europe/Athens'))
        for index, position in open_positions.iterrows():
            # Check to see if the trade has exceeded the time limit
            trade_open_dt = position['time'].replace(tzinfo = pytz.timezone('Europe/Athens'))
            deal_id = position['ticket']
            if(current_dt - trade_open_dt >= timedelta(hours = 2)):
                close_position(deal_id)
                
        for index, last in last_row.iterrows():
            #Exit strategy
            if(last['close'] < last['EMA'] and last['close'] > last['SMA']):
                close_positon_by_symbol(pair)

            lost_trade_count = calc_daily_lost_trades()

            if(lost_trade_count > strategy['maxLosses']):
                print("Daily losses have been exceeded. Not executing any more trades today")
                continue

            #Entry strategy
            if(last['close'] > last['EMA'] and last['close'] < last['SMA']):
                lot_size = calc_position_size(pair, strategy)
                open_position(pair, "BUY", lot_size, float (strategy['takeProfit']), float(strategy['stopLoss']))

In the last for loop, you are iterating over the newest tick data for the current period. Just before this loop starts, let’s calculate what kind of candlestick pattern you currently have. Find the corresponding candlestick pattern function by getting the candlestick pattern key defined in your strategy:

        candlestick_func = constants.cs_patterns[strategy['candlestickPattern']]

Now, you should call candlestick_func passing in the open, high, low, close data from the last_row data frame and assign the output of this function to last_row['is_cs_pattern']:

        last_row['is_cs_pattern'] = candlestick_func(last_row['open'], last_row['high'], last_row['low'], last_row['close'])

Running the candle stick function will simply give you a boolean TRUE or FALSE if the specified candlestick pattern has been detected or not.

Finally, go to the last loop in the check_trades method and find this code block:

            #Entry strategy
            if(last['close'] > last['EMA'] and last['close'] < last['SMA']):
                lot_size = calc_position_size(pair, strategy)
                open_position(pair, "BUY", lot_size, float (strategy['takeProfit']), float(strategy['stopLoss']))

You will want to modify the if condition to add if the candlestick pattern has been found or not. This can be done as follows:

            if(last['close'] > last['EMA'] and last['close'] < last['SMA'] and last['is_cs_pattern']):

Interested in creating your own algo trading bot? Check out my free series on algo trading here: https://www.conorjohanlon.com/category/algotrader/

That’s all for how to Detect candlestick patterns using TA-Lib and Python! As always, if you have any questions or comments please feel free to post them below. Additionally, if you run into any issues please let me know.

Leave a Comment

Your email address will not be published.

Subscribe to my newsletter to keep up to date with my latest posts

Holler Box